重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
什么是动态代理?
创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都做网站、成都网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的德清网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
先说下静态代理:
也即是说,在程序运行前,已经有了编译好的类,这个就是静态代理,
动态代理:
也即,在程序运行前, 代理类并不存在,而是在程序运行时,动态生成的类是动态代理类。
可能会有如下思考
代理模式是怎样的?用什么技术实现?
为什么要使用代理模式呢?有什么好处?
动态代理的应用场景有哪些?
先说说动态代理的实现吧:
动态代理主要使用的是Java 反射技术:
JavaAPI 中关于InvocationHandler有大致这么一段描述,每个代理类都要实现InvocationHandler这个接口,每个代理类都会关联到一个handle,
当一个代理类调用方法时,会转到InvocationHandler的Invoke方法进行调用。
以下代码有引用以为牛人代码
看下InvocationHandler源码,我这就不再写一遍了。
public interface InvocationHandler
{
public abstract Object invoke(Object obj, Method method, Object aobj[])
throws Throwable;
}
这个invoke方法中有几个参数 :
obj:表示我们要调用的真实对象
method:表示我们要调用的真实对象的某个方法。
aobj[] :表示真实对象某个方法接受的参数。
怎么用这个InvocationHandler 这个接口,主要看proxy这个类:
Proxy是创建一个代理对象的类
Proxy这个类有这个一个方法:是用的最多的方法
public static Object newProxyInstance(ClassLoader classloader, Class aclass[], InvocationHandler invocationhandler)
throws IllegalArgumentException
参数:
classloader: 定义了由那个ClassLoader对对象进行加载
aclass[]: 是为代理对象提供了哪些接口,表示代理对象实现了这些接口,
invocationhandler: 表示代理对象调用方法时会关联到那个invocationhandler
主要通过类Proxy与接口invocationhandler实现动态代理:
看下具体动态代理实现加深理解:
1.首先肯定是需要一个接口的:
public interface UserService {
public String getName(int id);
public Integer getAge(int id);
}
还需要这个接口的实现,也就是代理对象:
public class UserServiceImpl implements UserService {
@Override
public String getName(int id) {
System.out.println("------getName------");
return "Tom";
}
@Override
public Integer getAge(int id) {
System.out.println("------getAge------");
return 10;
}
}
还需要自定义一个invocationhandler ,自己实现invoke方法:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
MyInvocationHandler() {
super();
}
MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
if("getName".equals(method.getName())){
System.out.println("++++++before " + method.getName() + "++++++");
Object result = method.invoke(target, args);
System.out.println("++++++after " + method.getName() + "++++++");
return result;
}else{
Object result = method.invoke(target, args);
return result;
}
}
}
最后再看下proxy是怎样的使用这个接口,还有invocationhandler的
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class TestProxy {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
InvocationHandler invocationHandler = new MyInvocationHandler(userService);
UserService userServiceProxy = (UserService)Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), invocationHandler);
System.out.println(userServiceProxy.getName(1));
System.out.println(userServiceProxy.getAge(1));
}
}
以上可以看到可以清晰理解proxy的newproxyInstance是用来生成代理对象的, userService是指向具体 的实现
UserServiceImpl
给newproxyInstance方法传入实际的classLoader,实际类对应的接口,与调用类方法的invocationhandler即可实现:
System.out.println("++++++before " + method.getName() + "++++++");
其实上面这个一行代码就有***性质,aop时再详细讨论.
上面可以看到 使用这个反射技术实现动态代理,需要定义一个接口,但是有时候其实不想定义接口,只希望定义好实现类,然后你再在方法运行前,运行后加些内容即可。
这个就考虑了一个CGLIB这个可是不错的使用CGLIB实现动态代理很方便。
Cglib 采用的是ASM字节码技术,利用字节码生成代理类。效率比反射要高,但是不能代理final修身的类,clglib元素是集成被代理的类。final类不能被继承。
下面看下cglib具体实现:
下载 cglib包和asm包,你的工程需要依赖这两个包:
首先需要一个非final类,不再和反射一样需要一个接口了
public class StudentService {
public String getName(){
System.out.println("------getName------");
return "Tom";
}
public Integer getID(){
System.out.println("------getID------");
return 10;
}
}
接下来就看怎么代理上面的类了,需要定义一个拦截器:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("++++++before " + methodProxy.getSuperName()
+ "++++++");
System.out.println(method.getName());
Object o1 = methodProxy.invokeSuper(o, args);
System.out.println("++++++before " + methodProxy.getSuperName()
+ "++++++");
return o1;
}
}
上面的interceptf方法和invocationhandler中的invoke方法类似,通过interceptf来真正调用方法invokeSuper:
最后看下Cglb具体怎么代理StudentService这个类吧:
import net.sf.cglib.proxy.Enhancer;
public class TestCglib {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(StudentService.class);
enhancer.setCallback(cglibProxy);
StudentService o = (StudentService)enhancer.create();
System.out.println(o.getName());
// System.out.println(o.getID());
}
}
上面的o即是 代理类,该类可以访问真实的Studentservice方法:
运行结果:无锡妇科检查多少钱 http://www.120csfkyy.com/
++++++before CGLIB$getName$1++++++
getName
------getName------
++++++before CGLIB$getName$1++++++
Tom
使用Cglib 有啥好处?
看下面代码:
public class TestCglib {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(StudentService.class);
enhancer.setCallback(cglibProxy);
StudentService o = (StudentService)enhancer.create();
System.out.println(o.getName());
// System.out.println(o.getID());
Enhancer enhancer1 = new Enhancer();
enhancer1.setSuperclass(UserServiceImpl.class);
enhancer1.setCallback(cglibProxy);
UserServiceImpl obj = (UserServiceImpl)enhancer1.create();
System.out.println(obj.getName(1));
// System.out.println(o.getID());
}
}
只要写一个
cglibProxy
就可以是实现
对UserServiceImpl 与 StudentService进行代理
减少了代码的繁琐,只有有这么一个代理就够了,其次方便维护,解耦合,要是有问题,只要去找 原始类就可以了