代理模式
- 代理模式
- 静态代理
- 动态代理
- JDK动态代理
- CGlib动态代理
代理模式
创建一个代理对象来控制对原始对象的访问,可以用来扩展原始对象的功能,同时保护原始对象
一般使用代理模式的目的有两个:
- 保护目标对象
- 增强目标对象
代理模式有两种实现方案:静态代理 和 动态代理
下面以Dao层执行sql语句为例,讲述代理模式的 应用
静态代理
静态代理就是通过组合/继承的方式,代理类关联原始类,通过调用代理类的方法来实现 原始类的方法增强
这里有一个UserDaoImpl,作为原始类,提供一个模拟插入用户的接口
public interface UserDao {void insert();
}
/*****************************************************/public class UserDaoImpl implements UserDao{public void insert(){System.out.println("插入一个用户");}
}
创建一个静态代理类,StaticProxyUserDao,完成对userDao的增强
public class StaticProxyUserDao implements UserDao{private UserDao userDao;public StaticProxyUserDao(UserDao userDao) {this.userDao = userDao;}@Overridepublic void insert(){//事务开启...System.out.println("事务开启");userDao.insert();//方法增强...//事务提交...System.out.println("事务提交");}
}
测试代码如下,很容易理解,就不解释了
@Test
public void staticProxyTest(){//直接使用UserDao userDao = new UserDaoImpl();userDao.insert();System.out.println("---------------------------------------------------");//通过静态代理的方式,增强insert()方法StaticProxyUserDao staticProxyUserDao = new StaticProxyUserDao(userDao);staticProxyUserDao.insert();
}输出:
插入一个用户
---------------------------------------------------
事务开启
插入一个用户
事务提交
动态代理
通过静态代理的方式可以实现方法的增强,但对于每个增强的类,都需要再写一个代理类,这样显得代码比较臃肿,所以就有了动态代理,可以在程序执行过程中,动态的代理目标类。
动态代理有两种实现方式:JDK动态代理和CGlib动态代理
JDK动态代理
JDK动态代理是Java标准库中提供的一种代理方式
JDK动态代理是依赖接口的。
实现方式:通过Proxy类中的 静态方法 newProxyInstance(…)实现
@Test
public void JdkProxyTest(){UserDao userDao = new UserDaoImpl();/** 参数1:指定一个类加载器* 参数2:指定接口 getInterfaces()* 参数3:指定对应的代理方法* */System.out.println();UserDao proxyUserDao = (UserDao)Proxy.newProxyInstance(UserDao.class.getClassLoader(), UserDaoImpl.class.getInterfaces(), (proxy, method, args) -> {System.out.println("事务开启");Object invoke = method.invoke(userDao,args);System.out.println("事务关闭");return invoke;});proxyUserDao.insert();
}
可以看出来,JDK动态代理的方法 必须在接口中有定义,所以,如果想要代理没有在接口中定义的方法,这就需要使用CGlib来实现
CGlib动态代理
CGlib 是基于 类继承实现的,通过继承目标类 来实现类的代理,所以目标类必须是可继承的
CGlib支持更细粒度的代理——针对某个方法进行代理
CGLIB是第三方提供的包,需要引入Jar包
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.1</version>
</dependency>
spring-core jar包里包含cglib ,所以使用spring框架的工程可以直接使用。
实现方式:通过Enhancer类实现,如下:
@Test
public void CGlibProxyTest(){// 创建Enhancer对象 类似于 Proxy类Enhancer enhancer = new Enhancer();// 设置父类的字节码对象 即需要代理的类enhancer.setSuperclass(UserDaoImpl.class);//设置回调函数,增强方法/*** o 代理对象,即生成的代理类的实例,这里是生成的代理对象,不是被代理对象* method 被代理方法的Method对象* args 方法调用时传递的参数数组* methodProxy 方法代理对象*/enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {System.out.println("事务开启");//这里注意使用的是 invokeSuper() 调用的是父类的接口Object invoke = methodProxy.invokeSuper(o,objects);System.out.println("事务关闭");return invoke;});// 生成代理对象UserDaoImpl proxy = (UserDaoImpl) enhancer.create();proxy.insert();
}
这里提一下注意的点:
JDK动态代理,依赖接口完成代理,没有实例对象,所以需要传入实例对象,
即 method.invoke(userDao,args); 中的userDao,所以在代理之前,需要先有个实例化的对象而CGlib动态代理,依赖类继承实现,本身是知道代理对象的所有结构的,所以不需要传入实例对象,所以:
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println(“事务开启”);
Object invoke = methodProxy.invokeSuper(o,objects);
System.out.println(“事务关闭”);
return invoke;
});
第一个参数是 代理对象,而不是被代理对象,调用方法使用的是 invokeSuper() 不是invoke(),调用的是父类的接口
关于CGlib和JDK代理的详细代理过程,可以看下这篇文章
https://blog.csdn.net/ren9436/article/details/125602288