一、什么是动态代理,动态代理能干什么?
简单的说,动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。
举个栗子,比如你创建了一个类1,里面定义了一个计算a+b的值的方法1,假如你需要在类2中计算a+b+c的值,这个时候你不想再new一个方法,因为前面的a+b的方法,可以做前面一部分的工作了。但是你又不能直接去修改前面那个a+b的方法,因为很多其他的地方用到过方法1,一旦修改了方法1,必然会牵扯到很多不必要的麻烦。这个时候你就可以用动态代理的方法去增强类1中的方法1实现a+b+c的计算然后在类2中使用。当然实际的应用场景可能比这复杂得多。
具体参考传智教育什么是动态代理?两种常用的动态代理方式
二、两种常用的动态代理方式
- 基于接口的动态代理(jdk动态代理)
- 基于类的动态代理(CGLib动态代理)
三、jdk动态代理
实现方法
只需要调用java.lang.reflect.Prxy类中的newProxyInstance(ClassLoader loader, class<?>[] interfaces, InvocationHandler h)方法
该方法是一个static方法,共有三个参数
第一个参数:类加载器
第二个参数:增强方法所在类实现的接口,支持多个接口(数组形式)。
第三个参数:实现InvocationHandler接口,创建代理对象,写增强的部分。
代码示例
-
创建接口,定义方法,这里我创建的是一个UserDao接口,定义了add(int a,int b)方法和update(String id)方法。
package spring5; public interface UserDao { public int add(int a,int b); public String update(String id); }
-
创建接口实现类,实现定义的两个方法。
package spring5; public class UserDaoImpl implements UserDao{ @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
-
创建一个测试方法,增强方法,有两种写法
(1)创建一个类去实现InvocationHandler接口
在类中创建要增强类的对象,这里为了提高通用性将对象创建为了Object类。
class UserDaoProxy implements InvocationHandler { private Object obj; public UserDaoProxy(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System.out.println("方法之前执行......"+method.getName()+"传递的参数..."+ Arrays.toString(args)); //被增强的方法执行后再加上一个 c=1 //method.invoke(obj,args)意思为执行该方法 Object res = (Integer)method.invoke(obj,args)+1; return res; } }
然后调用newProxyInstance方法,第三个参数直接new一个该类对象。
@Test public void test1(){ Class[] interfaces = {UserDao.class}; UserDao dao = (UserDao) Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(),interfaces,new UserDaoProxy(new UserDaoImpl())); int res = dao.add(1,2); System.out.println("res:"+res); }
可以看到,调用该方法的结果变成了4,也就是说实现了a+b+c的计算。
(2)匿名内部类实现。
@Test public void test1(){ Class[] interfaces = {UserDao.class}; // UserDao dao = (UserDao) Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(),interfaces,new UserDaoProxy(new UserDaoImpl())); UserDao dao = (UserDao)Proxy.newProxyInstance(UserDaoImpl.class.getClassLoader(), interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { proxy = new UserDaoImpl(); Object res = (Integer)method.invoke(proxy,args)+1; return res; } }); int res = dao.add(1,2); System.out.println("res:"+res); }
该方式同样能实现一样的功能。
四、CGLib动态代理
什么是CGLib
CGLIB是一个强大的、高性能的代码生成代码库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib
CGLib动态代理的优势
CGLIB动态代理相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。
步骤
- 添加cgliib依赖
- 创建增强器Enhancer类对象
- 调用Enhancer类对象的setSuperclass(Class cls)方法设置需要增强类的对象,参数为需要增强的类。
- 调用Enhancer类对象的回调方法enhancer.setCallback(MethodInterceptor interceptor),与jdk动态代理一样,有两种方法。
- 获取增强之后的代理对象
示例
cgliib依赖,这里用的版本是我一直在用的,应该还算稳定。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
这是我需要增强的类,为了与jdk动态代理区别开来,是一个没有实现接口的类
package spring5;
public class Book {
public int add(int a, int b) {
return a+b;
}
}
根据步骤测试代码
@Test
public void testCGLib(){
//创建增强器Enhancer类对象
Enhancer enhancer = new Enhancer();
//设置需要增强类的对象
enhancer.setSuperclass(Book.class);
//调用回调方法,这里使用的是匿名内部类的方法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object res = (Integer)methodProxy.invokeSuper(o,objects)+100;
return res;
}
});
//获取增强后的类对象
Book book = (Book) enhancer.create();
//调用方法
System.out.println(book.add(1,2));
}
运行结果
当然CGLib还有一些其他好用的功能,这里只是使用了它的动态代理。
以上就是动态代理的两种方法,如有错误欢迎指正。