代理模式
定义
- 为其他对象提供一种代理,以控制对这个对象的访问。
- 代理对象在客户端和目标对象之间起到中介作用。
- 代理模式属于结构型设计模式,分为静态代理和动态代理
目的
- 保护目标对象
- 增强目标对象
静态代理
特点
- 程序运行前代理类的.class文件就已经存在
- 装饰者模式就是静态代理的一种体现。
案例
- 被代理对象
package test23;
/**
* 人有很多行为,比如谈恋爱
*/
public interface Person {
void findLove();
}
package test23;
public class Son implements Person {
@Override
public void findLove() {
System.out.println("儿子要求:肤白貌美");
}
}
- 代理对象
package test23;
/**
* 父亲要帮儿子相亲
*/
public class Father {
private Son son;
public Father(Son son) {
this.son = son;
}
public void findLove(){
System.out.println("父亲物色对象");
son.findLove();
System.out.println("双方同意交往");
}
}
- 测试类
package test23;
public class Main {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}
- 运行结果如下
缺点
- 大量的代码重复。静态代理类和目标类实现了相同的接口,代理类通过目标类实现了相同的方法。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 静态代理对象只服务于一种类型的对象。 如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
动态代理
动态代理和静态代理的基本思路一致,只不过动态代理功能更加强大,随着业务的扩展适应性更强。
特点
字节码随用随创建,随用随加载。
动态代理的方式
- 基于接口JDK代理
提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口。 - 基于子类CGLIB代理
提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。
jdk实现方式
jdk动态代理生成对象的步骤
- 1.获取被代理对象的引用,并且获取它的所有接口,反射获取
- 2.jdk动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口
- 3.动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用。
- 4.编译新生成的Java代码.class文件
- 5.重新加载到JVM中运行。
案例
package test24;
/**
* 人有很多行为,比如谈恋爱
*/
public interface Person {
void findLove();
}
- 被代理对象(目标对象)
package test24;
public class Customer implements Person {
@Override
public void findLove() {
System.out.println("高富帅");
}
}
- 代理对象
package test24;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKMeipo implements InvocationHandler {
//被代理的对象,把引用保存下来
private Object target;
public Object getInstance(Object target) {
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("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after() {
System.out.println("如果合适的话,就准备办事");
}
}
- 测试类
package test24;
public class Main {
public static void main(String[] args) {
try {
Person obj = (Person)new JDKMeipo().getInstance(new Customer());
obj.findLove();
}catch (Exception e){
e.printStackTrace();
}
}
}
- 运行结果如下
Cglib代理调用
案例
- maven项目引入以下依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
package test25;
/**
* 人有很多行为,比如谈恋爱
*/
public interface Person {
void findLove();
}
- 被代理对象(目标对象)
package test25;
public class Customer implements Person {
@Override
public void findLove() {
System.out.println("高富帅");
}
}
- 代理对象
package test25;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
Enhancer enhancer = new Enhancer();
//要把那个设置为即将生成的新类的父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before() {
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after() {
System.out.println("如果合适的话,就准备办事");
}
}
- 测试类
package test25;
public class CglibTest {
public static void main(String[] args) {
try{
Customer obj = (Customer) new CglibMeipo().getInstance(Customer.class);
obj.findLove();
}catch (Exception e){
e.printStackTrace();
}
}
}
- 运行结果如下
Cglib和jdk动态代理对比
- jdk动态代理实现了被代理对象的接口,Cglib代理继承了被代理对象。
- jdk动态代理和Cglib代理在运行期生成字节码,jdk动态代理直接写Class字节码,Cglib代理使用asm框架写Class字节码,Cglib代理实现更复杂,生成代理类比jdk动态代理效率低。
- jdk的动态代理调用代理方法是通过反射机制调用的,Cglib代理是通过FastClass机制直接调用的,Cglib代理的执行效率更高。
代理模式与Spring
代理模式在Spring源码中的应用
ProxyFactoryBean核心方法getObject(),源码如下:
/**
* Return a proxy. Invoked when clients obtain beans from this factory bean.
* Create an instance of the AOP proxy to be returned by this factory.
* The instance will be cached for a singleton, and create on each call to
* {@code getObject()} for a proxy.
* @return a fresh AOP proxy reflecting the current state of this factory
*/
@Override
public Object getObject() throws BeansException {
initializeAdvisorChain();
if (isSingleton()) {
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}