动态代理
动态代理是由java内部的反射机制来实现的,被代理对象必须要实现接口,才能产生代理对象.如果没有接口将不能使用动态代理技术
例子
- 编写一个接口,包含了增删改查4个方法
package cn.itcast.service;
public interface UserService {
void save();
void delete();
void update();
void find();
}
- 编写该接口的实现类
如果单纯的对每个方法都进行打开事物和提交事物,一旦方法很多,就会很繁琐,所以就使用动态代理技术,产生一个代理对象,代理对象帮我们增强代码。
package cn.itcast.service;
public class UserServiceImpl implements UserService {
@Override
public void save() {
//System.out.println("打开事物!");
System.out.println("保存用户!");
//System.out.println("提交事物!");
//int i = 1/0;
}
@Override
public void delete() {
System.out.println("删除用户!");
}
@Override
public void update() {
System.out.println("更新用户!");
}
@Override
public void find() {
System.out.println("查找用户!");
}
}
- 编写一个动态代理的类
需要实现InvocationHandler接口
package cn.itcast.c_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;
public class UserServiceProxyFactory implements InvocationHandler {
private UserService us;
//在创建代理类的时候,将需要代理的类传递过来,进行增强
public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
}
//自定义一个方法,用于产生一个动态代理对象
public UserService getUserServiceProxy(){
//newProxyInstance()包含了三个参数
//第一个是当前类的类加载器,第二个是需要被代理的类的接口,第三个是InvocationHandler接口(这里我们实现了这个接口,直接使用this即可)
UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
this);
//返回当前代理对象
return usProxy;
}
//重写InvocationHandler中的方法,用于增强代理对象中的方法
//invoke包含三个参数,第一个是当前的代理对象,第二个是当前调用的方法,当前方法执行时的参数
@Override
public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
System.out.println("打开事务!");
//invoke方法的执行需要当前方法所在对象的实例
Object invoke = method.invoke(us, arg2);//这里代表着,执行被代理对象中原有的方法
System.out.println("提交事务!");
return invoke;
}
}
- 测试
@Test
//动态代理
public void fun1(){
UserService us = new UserServiceImpl();
UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//代理对象与被代理对象实现了相同的接口
//代理对象 与 被代理对象没有继承关系,是兄弟关系
System.out.println(usProxy instanceof UserServiceImpl );//false
}
Cglib代理(了解)
cglib动态代理底层则是借助asm来实现的。第三方代理技术,cglib代理.可以对任何类生成代理.代理的原理是对目标对象进行继承代理. 如果目标对象被final修饰.那么该类无法被cglib代理。
- 编写Cglib代理类
package cn.itcast.c_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import cn.itcast.service.UserService;
import cn.itcast.service.UserServiceImpl;
//观光代码=>cglib代理
public class UserServiceProxyFactory2 implements MethodInterceptor {
public UserService getUserServiceProxy(){
Enhancer en = new Enhancer();//帮我们生成代理对象
en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
en.setCallback(this);//代理要做什么
UserService us = (UserService) en.create();//创建代理对象
return us;
}
@Override
public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
//打开事务
System.out.println("打开事务!");
//调用原有方法
Object returnValue = methodProxy.invokeSuper(prxoyobj, arg);
//提交事务
System.out.println("提交事务!");
return returnValue;
}
}
- 测试
@Test
public void fun2(){
UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
UserService usProxy = factory.getUserServiceProxy();
usProxy.save();
//判断代理对象是否属于被代理对象类型
//代理对象继承了被代理对象=>true
System.out.println(usProxy instanceof UserServiceImpl );//true
}
结论
总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。。