说到代理模式,你最先想到的是什么?在实际项目中,你有单独写过代理模式吗?spring中的动态代理又有哪些类型?有什么区别?
代理模式(proxy pattern),可以简单理解为一个类代理另一个类的功能。它是一种结构型模式。代理模式分为:静态代理和动态代理。
业务场景:想要保护一个对象时,想要增强一个对象时,都可以使用代理模式。
关键代码:代理对象实现了被代理类的接口,并且持有被代理对象的引用。
下面看UML类图:
代码实现步骤:
1.形状接口;
/**
* 1.形状接口
* @author 程就人生
* @Date
*/
public interface IShape {
public void draw();
}
2.接口实现类;
/**
* 2.矩形继承了形状接口;
* @author 程就人生
* @Date
*/
public class Rectangle implements IShape{
// 宽度
private int width;
// 高度
private int height;
@Override
public void draw() {
System.out.println("rectangle:width=" + width + ",height=" + height);
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
3.矩形代理类,静态代理类;
public static void main(String[] argo){
Rectangle rectangle = new Rectangle();
rectangle.setHeight(10);
rectangle.setWidth(5);
IShape shape = new RectangleProxy(rectangle);
shape.draw();
}
测试代码;
public static void main(String[] argo){
Rectangle rectangle = new Rectangle();
rectangle.setHeight(10);
rectangle.setWidth(5);
IShape shape = new RectangleProxy(rectangle);
shape.draw();
}
测试结果;
提前做点什么
rectangle:width=5,height=10
之后做点什么
这段代码的意思是:为矩形加了一个代理类,在代理类中对画的方法进行了增强,画之前做了一点事,画之后又做了一点事。
4.动态代理类,JDK实现方式;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理 - JDK实现方式
* @author 程就人生
* @Date
*/
public class RectangleProxy2 implements InvocationHandler {
// 被代理对象
private Object target;
//通过反射机制获取对象,获取接口
public Object getInstance(Object target) throws Exception{
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 业务增强
before();
Object obj = method.invoke(this.target, args);
after();
return obj;
}
private void before(){
System.out.println("提前做点什么");
}
private void after(){
System.out.println("之后做点什么");
}
}
测试代码:
public static void main(String[] argo){
Rectangle rectangle = new Rectangle();
rectangle.setHeight(10);
rectangle.setWidth(5);
// 动态代理测试1
IShape shape = (IShape) new RectangleProxy2().getInstance(rectangle);
shape.draw();
}
测试结果:
提前做点什么
rectangle:width=5,height=10
之后做点什么
5.动态代理类,CGLib实现方式;
/**
* 无继承的被代理类
* @author 程就人生
* @Date
*/
public class Rectangle2 {
// 宽度
private int width;
// 高度
private int height;
public void draw() {
System.out.println("rectangle:width=" + width + ",height=" + height);
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* 动态代理 - CGlib
* @author 程就人生
* @Date
*/
public class RectangleProxy3 implements MethodInterceptor {
//通过反射机制获取对象,获取接口
public Object getInstance(Class<?> clazz) throws Exception{
Enhancer enhancer = new Enhancer();
// 即将生成的新类的父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
private void before(){
System.out.println("提前做点什么");
}
private void after(){
System.out.println("之后做点什么");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 业务增强
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
}
测试代码:
// 动态代理测试2
Rectangle2 rectangle2 = (Rectangle2) new RectangleProxy3().getInstance(Rectangle2.class);
rectangle2.draw();
测试结果:
提前做点什么
rectangle:width=0,height=0
之后做点什么
静态代理和动态代理的区别:
- 静态代理只能手动完成代理操作,如果被代理类增加了新方法,代理类需要同步增加,违背了开闭原则。
- 动态代理采用运行时生成代码的方式,取消了被代理类的扩展限制,遵循开闭原则。
最后总结
上面代码使用了两种实现动态代理的方式,第一种被代理对象有接口,第二种被代理对象无接口,所以Spring中代理的选择是:
- 当bean有实现接口时,Spring就会用JDK动态代理;
- 当bean没有实现接口时,Spring就会选择CGLib代理;
- 可以通过设置aspectj-autoproxyd的proxy-target-class为true,来强制使用CGLib代理。
网友评论