解密动态代理,cglib和jdk动态代理

现在说的动态代理,无非是两种,一种是cglib和jdk原生的动态代理

 

CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。
CGLIB代理主要通过对字节码的操作,为对象引入间接级别,以控制对象的访问。我们知道Java中有一个动态代理也是做这个事情的,那我们为什么不直接使用Java动态代理,而要使用CGLIB呢?答案是CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了,在spring中接口默认走的是jdk动态代理,其余都是走的cglib动态代理。

动态代理到底是什么呢,为什么又叫动态代理呢。我们先看看设计模式中的代理模式。

代理模式分两种,静态代理和动态代理

先看一下静态代理怎么用,这里直接从网上拷贝一些内容

定义接口UserManager

public interface UserManager{
    public void addUser(String userId, String userName)  
  
      
    public void delUser(String userId) 
}

接口实现类UserManagerImpl 

public class UserManagerImpl implements UserManager {  
  
    @Override  
    public void addUser(String userId, String userName) {  
        System.out.println("UserManagerImpl.addUser");  
    }  
  
    @Override  
    public void delUser(String userId) {  
        System.out.println("UserManagerImpl.delUser");  
    }  

}

接口实现类的代理类UserManagerImplProxy 

public class UserManagerImplProxy implements UserManager {  
  
    // 目标对象  
    private UserManager userManager;  
    // 通过构造方法传入目标对象  
    public UserManagerImplProxy(UserManager userManager){  
        this.userManager=userManager;  
    }  
    @Override  
    public void addUser(String userId, String userName) {  
        try{  
                //添加打印日志的功能  
                //开始添加用户  
                System.out.println("start-->addUser()");  
                userManager.addUser(userId, userName);  
                //添加用户成功  
                System.out.println("success-->addUser()");  
            }catch(Exception e){  
                //添加用户失败  
                System.out.println("error-->addUser()");  
            }  
    }  
  
    @Override  
    public void delUser(String userId) {  
        userManager.delUser(userId);  
    }  
  
}  

测试类

public class Client {  
  
    public static void main(String[] args){  
        //UserManager userManager=new UserManagerImpl();  
        UserManager userManager=new UserManagerImplProxy(new UserManagerImpl());  
        userManager.addUser("1111", "张三");  
    }  
} 

 

静态代理的优势和劣势

静态代理就是一个代理类UserManagerImplProxy 和被代理对象UserManagerImpl 实现同一个接口,代理类持有被代理对象的引用,在代理类中调用被代理对象的相关方法,并且在调用前后执行其他逻辑,比如记录日志。

根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类,又或者说UserManagerImpl这个类想要添加修改用户的功能,UserManagerImplProxy这个代理类也需要修改,这里不符合设计模式原则,对扩展开放,对修改关闭。

下面我们看个需求

现在需求如下,我想要管理用户,现有功能是可以删除和添加用户基本信息,未来的规划想要扩展其他功能比如说修改用户基本信息。并且,需要单独管理用户的家庭情况。最后,我还需要在每次操作的时候记录操作内容和操作时间。

如果用静态代理模式,我除了需要新建代理类UserManagerImpl和UserFamilyManagerImpl,还需要新建两个实现类的代理类,在两个代理类中操做的时候记录操作内容和操作时间。并且在未来添加修改用户基本信息的时候,还需要对UserManagerImpl和UserManagerImplProxy修改,这样看起来是不是对于代码和程序的维护成本较高。

有人会问了,我直接新建UserManagerImpl的时候加上修改用户的功能不久行了,并且不用静态代理,我也能在UserManagerImpl中记录操作内容和操作时间呀。以上,我只是举个例子,来方便说明一定的问题,但是实际生产上,未来的规划是不可预期的(修改用户),并且在设计模式中也提到职责单一原则,也就是专人干专事。用户管理类就管理用户,其他需求(记录日志)需要其他类实现。

综上可以看到,静态代理的劣势相对还是比较明显的,这里引入动态代理,我们就会想办法可以通过一个代理类完成全部的代理功能,

在上面的示例中,一个代理只能代理一种类型(用户管理和用户家庭管理需要两个代理类),而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。

JDK动态代理实现用户管理和用户家庭管理

先看看怎么用

新建用户管理接口和用户家庭管理接口,这里我们简单模拟一下就好了。

public interface UserManager{
    public void addUser(String userId, String userName)  
  
      
    public void delUser(String userId) 
}

public interface UserFamilyManager{
   public int getFamilyNumber();

}

新建用户管理接口实现和用户家庭管理接口实现,简单模拟一下。

public class UserMangerImpl implements UserManager {

	public void addUser(final String userId, final String userName) {
		System.out.println("新增用户");

	}

	public void delUser(final String userId) {
		System.out.println("删除用户");

	}

public class UserFamilyManagerImpl implements UserFamilyManager {

	public int getFamilyNumber() {
		System.out.println("获得家庭人数");
		return 5;
	}

}

jdk动态代理,实现了InvocationHandler就行了,新建代理类,实现对UserManager和UserFamilyManager代理

public class UserJdkProxy implements InvocationHandler {
	private Object target;

	public UserJdkProxy(final Object um) {
		target = um;
	}

	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
		//模拟记录日志
		System.out.println("调用了" + method.getName() + "调用时间是" + new Date());
		Object obj = method.invoke(target, args);
	 //模拟记录日志
		System.out.println("操作完成时间是" + new Date());
		return obj;
	}

}

测试类

@Test
public void cap1() {
	UserManager userManager = (UserManager) Proxy.newProxyInstance(UserManager.class.getClassLoader(),
	new Class[] { UserManager.class }, new UserJdkProxy(new UserMangerImpl()));
	UserFamilyManager userFamilyManaUser = (UserFamilyManager) Proxy.newProxyInstance(
	UserFamilyManager.class.getClassLoader(), new Class[] { UserFamilyManager.class },
	new UserJdkProxy(new UserFamilyManagerImpl()));
	int i = userFamilyManaUser.getFamilyNumber();
	userManager.addUser("k", "xiong");
}

测试结果
调用了getFamilyNumber调用时间是Thu Aug 22 16:03:40 CST 2019
获得家庭人数
操作完成时间是Thu Aug 22 16:03:40 CST 2019
调用了addUser调用时间是Thu Aug 22 16:03:40 CST 2019
新增用户
操作完成时间是Thu Aug 22 16:03:40 CST 2019

其中代理的目的就是业务逻辑和业务逻辑不相关进行解耦。在进行动态代理后,如果对UserManager想要增加修改用户的功能,直接修改UserManager类就行,不用修改代理类。

Cglib动态代理实现用户管理和用户家庭管理

maven项目中需要在pom.xml添加jar包依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

新建代理类

public class UserCglibProxy implements MethodInterceptor {
	private Object target;

	public UserCglibProxy(final Object target) {
		this.target = target;
	}

	public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy)
			throws Throwable {
		// 模拟记录日志
		System.out.println("调用了" + method.getName() + "调用时间是" + new Date());
		 //下面method.invoke和roxy.invokeSuper都可以达到调用原有类比如UserManagerImpl的方法
		// Object obj1 = method.invoke(target, args);
		Object obj1 = proxy.invokeSuper(obj, args);
		// 模拟记录日志
		System.out.println("操作完成时间是" + new Date());
		return obj1;
	}

}

新建测试类

@Test
public void cap2() {
	Enhancer enhancer = new Enhancer();
	enhancer.setSuperclass(UserFamilyManagerImpl.class);
	enhancer.setCallback(new UserCglibProxy(new UserFamilyManagerImpl()));
	UserFamilyManager userFamilyManaUser = (UserFamilyManager) enhancer.create();
	int i = userFamilyManaUser.getFamilyNumber();
	Enhancer enhancer2 = new Enhancer();
	enhancer2.setSuperclass(UserManagerImpl.class);
	enhancer2.setCallback(new UserCglibProxy(new UserManagerImpl()));
	UserManager userManager = (UserManager) enhancer2.create();
	userManager.addUser("k", "xiong");
}
测试结果如下:
调用了getFamilyNumber调用时间是Thu Aug 22 16:30:44 CST 2019
获得家庭人数
操作完成时间是Thu Aug 22 16:30:44 CST 2019
调用了addUser调用时间是Thu Aug 22 16:30:44 CST 2019
新增用户
操作完成时间是Thu Aug 22 16:30:44 CST 2019

动态代理扩展

jdk动态代理和cglib动态代理的区别:

1.jdk通过Proxy.newProxyInstance后面的参数,来设置被代理的类和代理类,而cglib通过enhancer.setSuperclass和setCallBack来设置被代理的类和代理类

2.jdk只能代理接口,而cglib可以代理任何objcet,这也是spring为什么在aop实现的时候,为什么用了两种代理,其中接口默认走jdk动态代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值