代理模式
代理模式的三种实现方式
- 静态代理
- JDK动态代理
- CGlib动态代理
1、静态代理
- 目标对象
- 代理对象
- 接口
实现关键点:目标对象与代理对象实现同一接口,将目标对象作为成员变量引入到代理对象中,实现目标对象执行前或执行后的额外处理。
例子
保存用户
Dao 直接保存
DaoProxy 给保存的方法增加事务
代码实现
接口类
package com.jkxy.design01.proxy.staticproxy.dao;
/**
* @author Jcon
* @version V1.0
* @Description: (接口类)
* @date 2019年01月27日
*/
public interface IDao {
void save();
}
目标对象
package com.jkxy.design01.proxy.staticproxy.dao;
/**
* @author Jcon
* @version V1.0
* @Description: (目标对象)
* @date 2019年01月27日
*/
public class UserDao implements IDao {
@Override
public void save() {
System.out.println("------保存用户到数据库-------");
}
}
代理对象
package com.jkxy.design01.proxy.staticproxy.dao;
/**
* @author Jcon
* @version V1.0
* @Description: (代理对象)
* @date 2019年01月27日
*/
public class UserDaoProxy implements IDao {
private IDao target;
public UserDaoProxy(IDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("---事务开始----");
target.save();
System.out.println("---事务结束----");
}
}
测试类
package com.jkxy.design01.proxy.staticproxy.dao;
/**
* @author Jcon
* @version V1.0
* @Description: (测试类)
* @date 2019年01月27日
*/
public class App {
public static void main(String[] args) {
UserDao userDao = new UserDao();
new UserDaoProxy(userDao).save();
}
}
总结
优点:可以做到在不修改目标对象功能的前提下,对目标对象功能扩展
缺点:①每个目标对象都需要有一个代理对象,类爆炸 ②目标对象和代理对象必须实现同一个接口 ③一旦接口增加方法,目标对象与代理对象都要维护增加的方法
静态代理缺点解决
动态代理
2、JDK动态代理
- 目标对象
- 代理对象
- 接口
JDK动态代理实现细节
- 代理对象不需要实现接口,目标对象必须实现接口
- 代理对象的生成,是利用JDK API,动态的在内存中构建代理对象(需要我们指定创建爱代理对象实现的接口类型)
接口类
package com.jkxy.design01.proxy.dynamic;
/**
* @author Jcon
* @version V1.0
* @Description: (接口类)
* @date 2019年01月27日
*/
public interface IDao {
void save();
}
目标对象
package com.jkxy.design01.proxy.dynamic;
/**
* @author Jcon
* @version V1.0
* @Description: (目标对象)
* @date 2019年01月27日
*/
public class UserDao implements IDao {
@Override
public void save() {
System.out.println("------保存用户到数据库-------");
}
}
代理对象,JDK动态代理,代理对象不需要实现接口
package com.jkxy.design01.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author Jcon
* @version V1.0
* @Description: (代理对象代理工厂 - 动态代理)
* @date 2019年01月27日
*/
public class ProxyFactory {
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(
// 指定当前目标对象使用类加载器
target.getClass().getClassLoader(),
// 目标对象实现的接口类型
target.getClass().getInterfaces(),
// 事件处理器
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("---开启事务---");
// 执行目标对象方法
Object result = method.invoke(target, args);
System.out.println("---提交事务---");
return result;
}
}
);
}
}
使用泛型写法
package com.jkxy.design01.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author Jcon
* @version V1.0
* @Description: (代理对象 , 使用泛型写法)
* @date 2019年01月27日
*/
public class ProxyFactoryT<T> {
private T target;
public ProxyFactoryT(T target) {
this.target = target;
}
public T getProxyInstance(){
return (T)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----执行目标对象前业务处理---");
Object result = method.invoke(target, args);
System.out.println("----执行目标对象后业务处理---");
return result;
}
}
);
}
}
测试类
package com.jkxy.design01.proxy.dynamic;
/**
* @author Jcon
* @version V1.0
* @Description: (测试类)
* @date 2019年01月27日
*/
public class App {
public static void main(String[] args) {
// 创建目标对象
UserDao userDao = new UserDao();
// 创建代理对象
IDao proxyInstance = (IDao) new ProxyFactory(userDao).getProxyInstance();
proxyInstance.save();
System.out.println("=========使用泛型写法测试类========");
// 创建目标对象
UserDao userDao2 = new UserDao();
// 创建代理对象
IDao proxyInstance2 = new ProxyFactoryT<IDao>(userDao).getProxyInstance();
// 调用目标对象方法
proxyInstance2.save();
}
}
总结
优点:①代理对象不需要实现接口,不同目标对象都可以使用通用的代理对象
②弥补了静态代理中的类爆炸问题
缺点:如果目标对象没有实现接口的话则使用不了
3、cglib动态代理
- 目标对象
- 代理对象
Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。
CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉
代码实现
目标对象类
package com.jkxy.design01.proxy.cglib;
import com.jkxy.design01.proxy.dynamic.IDao;
/**
* @author Jcon
* @version V1.0
* @Description: (目标对象)
* @date 2019年01月27日
*/
public class UserDao implements IDao {
@Override
public void save() {
System.out.println("------保存用户到数据库-------");
}
}
代理对象类
package com.jkxy.design01.proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author Jcon
* @version V1.0
* @Description: (cglib代理对象 对UserDao 在内存中动态构建一个子类对象)
* @date 2019年01月27日
*/
public class ProxyFactory implements MethodInterceptor {
// 维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象创建代理对象
public Object getProxyInstance() {
// 1.工具类
Enhancer en = new Enhancer();
// 2.设置父类
en.setSuperclass(target.getClass());
// 3.设置回调函数
en.setCallback(this);
// 4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("-----执行目标对象前业务处理-----");
// 执行目标对象
Object result = method.invoke(target, args);
System.out.println("-----执行目标对象后业务处理------");
return result;
}
}
测试类
package com.jkxy.design01.proxy.cglib;
/**
* @author Jcon
* @version V1.0
* @Description: (cglib代理测试类)
* @date 2019年01月27日
*/
public class App {
public static void main(String[] args) {
// 目标对象
UserDao target = new UserDao();
// 创建代理对象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
// 执行代理对象的方法
proxy.save();
}
}
总结:
①需要引入cglib – jar文件, 但是spring的核心包中已经包括了cglib功能,所以直接引入spring-core.jar即可,引入功能包后,就可以在内存中动态构建子类
②代理的类不能为final, 否则报错。
③目标对象的方法如果为final/static, 那么就不会被拦截,即不会执行目标对象额外的业务方法
在Spring的AOP编程中:
- 如果加入容器的目标对象有实现接口,用JDK代理;
- 如果目标对象没有实现接口,用Cglib代理;

浙公网安备 33010602011771号