众所周知代理模式很是常用的,比如Spring、SpringAOP等,这些的底层都使用到了代理模式,代理模式又分为静态代理、动态代理,但如何理解代理模式呢?下方以代码进行实现:
目录
静态代理:
首先为静态代理,为什么叫静态?因为它是事先预定好的(已经创建好的),下方代码演示:
先创建两个目标接口:
UserMySQLDao:
public interface UserMySQLDao {
void addMySQLData();
void deleteMySQLData();
void updateMySQLData();
void selectMySQLData();
}
UserOracleDao:
public interface UserOracleDao {
void addOracleData();
void deleteOracleData();
void updateOracleData();
void selectOracleData();
}
再次创建两个目标对象并且实现它们:
UserMySQLDaoImpl:
public class UserMySQLDaoImpl implements UserMySQLDao {
@Override
public void addMySQLData() {
System.out.println("MySQL add 1 row true!");
}
@Override
public void deleteMySQLData() {
System.out.println("MySQL delete 1 row true!");
}
@Override
public void updateMySQLData() {
System.out.println("MySQL update 1 row true!");
}
@Override
public void selectMySQLData() {
System.out.println("MySQL select 1 row true!");
}
}
UserOracleDaoImpl:
public class UserOracleDaoImpl implements UserOracleDao {
@Override
public void addOracleData() {
System.out.println("Oracle add 1 row true!");
}
@Override
public void deleteOracleData() {
System.out.println("Oracle delete 1 row true!");
}
@Override
public void updateOracleData() {
System.out.println("Oracle update 1 row true!");
}
@Override
public void selectOracleData() {
System.out.println("Oracle select 1 row true!");
}
}
现在已经将目标接口与目标对象创建完成,并且目标对象已经实现了目标接口,我们来创建两个代理对象:
ProxyMySQLObject:
public class ProxyMySQLObject implements UserMySQLDao {
private UserMySQLDao userMySQLDao;
public ProxyMySQLObject(UserMySQLDao userMySQLDao) {
this.userMySQLDao = userMySQLDao;
}
@Override
public void addMySQLData() {
System.out.println("[Debug]:向 MySQL 数据库中添加一条数据...");
userMySQLDao.addMySQLData();
}
@Override
public void deleteMySQLData() {
System.out.println("[Debug]:向 MySQL 数据库中删除一条数据...");
userMySQLDao.deleteMySQLData();
}
@Override
public void updateMySQLData() {
System.out.println("[Debug]:向 MySQL 数据库中修改一条数据...");
userMySQLDao.updateMySQLData();
}
@Override
public void selectMySQLData() {
System.out.println("[Debug]:向 MySQL 数据库中查询一条数据...");
userMySQLDao.selectMySQLData();
}
}
ProxyOracleObject:
public class ProxyOracleObject implements UserOracleDao {
private UserOracleDao userOracleDao;
public ProxyOracleObject(UserOracleDao userOracleDao) {
this.userOracleDao = userOracleDao;
}
@Override
public void addOracleData() {
System.out.println("[Debug]:向 Oracle 数据库中添加一条数据...");
userOracleDao.addOracleData();
}
@Override
public void deleteOracleData() {
System.out.println("[Debug]:向 Oracle 数据库中删除一条数据...");
userOracleDao.deleteOracleData();
}
@Override
public void updateOracleData() {
System.out.println("[Debug]:向 Oracle 数据库中修改一条数据...");
userOracleDao.updateOracleData();
}
@Override
public void selectOracleData() {
System.out.println("[Debug]:向 Oracle 数据库查询一条数据...");
userOracleDao.selectOracleData();
}
}
在不改变目标接口的情况下进行功能迭代,我这边就以添加一个输出语句为例,我们进行测试:
//静态代理测试:操作Oracle的代理对象,静态代理:目标对象与代理对象都需要实现一样的接口。
public static void testProxyStaticOracle(){
ProxyOracleObject proxyOracleObject = new ProxyOracleObject(new UserOracleDaoImpl());
proxyOracleObject.addOracleData();
proxyOracleObject.deleteOracleData();
proxyOracleObject.selectOracleData();
proxyOracleObject.updateOracleData();
}
//静态代理测试:操作 MySQL 的代理对象,静态代理:目标对象与代理对象都需要实现一样的接口。
public static void testProxyStaticMySQL(){
ProxyMySQLObject proxyMySQLObject = new ProxyMySQLObject(new UserMySQLDaoImpl());
proxyMySQLObject.addMySQLData();
proxyMySQLObject.deleteMySQLData();
proxyMySQLObject.selectMySQLData();
proxyMySQLObject.updateMySQLData();
}
通过以上静态代理代码,由此可以发现:
静态代理可以在不改变目标对象的情况下进行功能迭代。但是随着业务的需要,当目标对象的新增,那么代理对象也需要新增,并且目标对象与代理对象都需要实现共同的一个接口,也就是后期维护较繁琐化。
动态代理—JDK代理:
我们再来看动态代理,动态代理与静态代理的区别就是动态代理是动态的在内存中创建代理对象,而不需要手动硬编码代理对象。
动态代理示例,我们以JDK代理为例,目标对象不变,我们创建一个代理处理器并且实现JDK自带的处理器InvocationHandler :
ProxyDynamicHandler:
public class ProxyDynamicHandler implements InvocationHandler {
private Object object;
public ProxyDynamicHandler(Object object) { //简易的修饰者模式动态创建对象
this.object = object;
}
//获取在内存创建的代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(object, args);
if(invoke != null){
return invoke;
}
return null;
}
}
测试:
public static void testProxt(){
//动态代理 代理 MySQL 目标对象
ProxyDynamicHandler proxyDynamicInvocationMySQLHandler = new ProxyDynamicHandler(new UserMySQLDaoImpl());
UserMySQLDao userMySQLDao = (UserMySQLDao) proxyDynamicInvocationMySQLHandler.getProxy();
System.out.println("向 MySQL 数据库查询数据");
userMySQLDao.selectMySQLData(); //这里拿到接口后,一样可以不改变源接口的基础下进行功能迭代,还是以输出语句为例,当然拿到接口后你想怎么操作就怎么操作
System.out.println("============分割线:下方为 代理 Oracle 目标对象");
//动态代理 代理 Oracle 目标对象
ProxyDynamicHandler proxyDynamicInvocationOracleHandler = new ProxyDynamicHandler(new UserOracleDaoImpl());
UserOracleDao userOracleDao = (UserOracleDao) proxyDynamicInvocationOracleHandler.getProxy();
System.out.println("向 Oracle 数据库中修改了一条数据!");
userOracleDao.updateOracleData();
}
运行结果:
向 MySQL 数据库查询数据
MySQL select 1 row true!
============分割线:下方为 代理 Oracle 目标对象
向 Oracle 数据库中修改了一条数据!
Oracle update 1 row true!
通过动态代理模式,得出结论:
动态代理不需要创建多个代理对象,它是动态的在内存中创建代理对象,动态代理同样可以保证不改变源代码的情况下进行功能迭代,并且维护对比静态代理更加方便、灵活。
动态代理—Cglib代理
Cglib代理与 JDK代理有一个很大的区别,那就是Cglib它是针对类进行代理的,对指定类所生成的一个子类,并进行方法覆盖,底层利用ASM开源包对字节码进行生成类,Cglib也可以对实现接口的类进行代理,同样也可以对抽象(使用了抽象必须对继承类进行代理,JDK代理不行),我们进行简单的测试,其它测试你可以自测。
创建一个类实现方法拦截器:
public class CglibProxyObject implements MethodInterceptor {
/**
* 参数解析:
* param1:被代理类
* param2:被代理方法
* param3:被代理参数
* param4:方法代理,代理谁
*/
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object o = proxy.invokeSuper(object, args);
return o;
}
}
创建一个简约的类:
public class Notepad {
void hello() {
System.out.println("Hello World!");
}
}
测试实现:
public static void test3() {
CglibProxyObject cglibProxyObject = new CglibProxyObject();
Enhancer enhancer = new Enhancer(); //Cglib 增强类
enhancer.setCallback(cglibProxyObject); //设置回调
enhancer.setSuperclass(Notepad.class);//代理类
Notepad notepad = (Notepad) enhancer.create();//使用ASM字节码生成指定类的子类
notepad.hello(); //Hello World!
}
结论
动态代理与静态代理的区别:
静态与动态代理都可以不改变源代码的情况下进行功能迭代
静态代理是需要 目标对象 与 代理对象实现同一个接口,如果随着业务的增多,代理对象也会增多,后期维护不方便,繁琐化,而且代理对象是事先创建好的,硬编码在程序中。
动态代理则是动态的在内存中创建代理对象,没有硬编码在程序中,使用起来更加方便、灵活,后期维护相比静态代理更加简单
JDK动态代理 与 Cglib动态代理的区别:
JDK代理只能对实现了接口的类进行代理,不能针对单纯的一个类来代理
Cglib它不仅可以对实现接口的类进行代理,还可以对继承进行代理,它主要是针对类进行代理,所指定的类生成一个子类,并覆盖它的方法,底层利用ASM开源包对字节码进行生成类
SpringAOP是如何在JDK代理与Cglib代理进行切换的?
如果一个Bean实现了接口,那么这时Spring是使用的JDK代理
如果一个Bean没有实现接口,这时的Spring是使用的Cglib代理
当然也可以配置只使用Cglib代理