一、动态代理
Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,代理对象存在的价值: 主要用于拦截对真实业务对象的访问。代理对象有什么方法: 一般来说,真实业务对象具有什么方法,那么代理对象就会具备相应的方法。
二、JDK 动态代理
-
Java 虚拟机运行图
Java 虚拟机运行图
Java 动态代理运行机制
JDK:
动态代理接口:InvocationHandler
动态代理类:Proxy
JDK动态代理4个条件:
目标接口、目标类、代理类、代理类里的拦截器 - 业务接口与业务类
// 接口
public interface UserService {
Boolean add(User user);
}
// 实现类
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public Boolean add(User user) {
if(userMapper.insertUser(user)>1){
return true;
}
return false;
}
}
- 代理类代码
public class LogHandler implements InvocationHandler {
// 我们要代理的目标对象
private Object targetObject;
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看
其三个参数 第一个参数
* handler.getClass().getClassLoader()
* ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为
代理对象提供的接口是真实对象所实行的接口
* ,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 第三
个参数handler, 我们这里将这个代理对象关联到了上方的
* InvocationHandler 这个对象上
*/
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
/**
* 实现接口的方法
*/
public Object invoke(Object proxy, Method method, Object[]
args) throws Throwable {
Object ret = null;
try {
System.out.println("正在进行操作前的准备工作……");
// 调用目标方法,代理对象与参数
ret = method.invoke(targetObject, args);
System.out.println("操作成功,正在进行确认处理……");
} catch (Exception e) {
e.printStackTrace();
System.out.println("error-->>" + method.getName());
throw e;
}
return ret;
}
}
- 代理测试
@Test
public void testAdd() {
User user = new User();
user.setParentId(2L);
LogHandler logHandler = new LogHandler();
UserService userServiceProxy = (UserService) logHandler.newProxyInstance(userService);
userServiceProxy.add(user);
}
三、CGLIB 动态代理
CGlib 是一个强大的,高性能,高质量的 Code 生成类库。它可以在运行期扩展 Java 类与实现 Java 接口。
用 CGlib 生成代理类是目标类的子类。
用 CGlib 生成 代理类不需要接口。
用 CGLib 生成的代理类重写了父类的各个方法。
拦截器中的 intercept 方法内容正好就是代理类中的方法体。
- Maven依赖
<!--添加cglib包动态代理 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
- 业务类
不需实现接口。
public class UserServiceImpl{
public void saveUser() {
System.out.println("保存用户信息");
}
public void updateUser(){
System.out.println("更新用户信息");
}
}
- 动态代理类
public class UserDaoInterceptor implements MethodInterceptor{
//目标类
private Object target;
/**
* 产生代理类
* @return
*/
public Object createProxy(){
//字节码增强技术
Enhancer enhancer = new Enhancer();
//设置代理类的父类是目标类
enhancer.setSuperclass(target.getClass());
//设置回调
enhancer.setCallback(this);
return enhancer.create();
}
public UserDaoInterceptor(Object target) {
this.target = target;
}
/**
* 等同于invoke方法
*/
public Object intercept(Object arg0, Method method, Object[]
arg2,
MethodProxy arg3) throws Throwable {
//前置通知
if(method.getName().equals("saveUser")
||method.getName().equals("updateUser")){
System.out.println("开启事务");
method.invoke(target, arg2);
System.out.println("提交事务");
}else{
method.invoke(target, arg2);
}
return null;
}
}
- 代理测试
public class UserDaoTest {
@Test
public void testCGLibProxy(){
Object target = new UserDaoImpl();
/**
* 创建一个拦截器
*/
UserDaoInterceptor interceptor = new
UserDaoInterceptor(target);
UserDaoImpl proxy = (UserDaoImpl)interceptor.createProxy();
proxy.saveUser();
}
}
spring 有两种代理方式:
1)若目标对象实现了若干接口,spring 使用 JDK 的 java.lang.reflect.Proxy 类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口
2)若目标对象没有实现任何接口,spring 使用 CGLIB 库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用 JDK 的动态代理好。
网友评论