java框架基础(3)
代理
静态代理:
接口a定义了方法aa,实现了接口a的实现类b和实现类c;如何用c来代理b;
其实就是把b类实例化后作为参数通过c类的构造函数注入到c类中,这样就可以在c类里调用b类里自己重写的aa方法了;
JDK动态代理:
这里涉及两个类:Proxy类和InvocationHandler类;
Proxy类用得最多的就是newProxyInstance 这个方法:
这个方法的作用就是得到一个动态的代理对象,其接收三个参数
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
package com.lzj; /** * @Description: TODO * @author: lzj * @date: 2021年04月27日 16:53 */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 动态代理举例 * * * **/ interface Human{ void getBelief(); void eat(String food); } //被代理类 class SuperMan implements Human{ @Override public void getBelief() { System.out.println("I believe I can fly!"); } @Override public void eat(String food) { System.out.println("我喜欢吃"+food); } } class ProxyFactory{ //调用此方法,返回一个代理类的对象,解决了问题1 public static Object getProxychInstance(Object obj){//obj:被代理类的对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } } class MyInvocationHandler implements InvocationHandler { private Object obj; public void bind (Object obj){ this.obj = obj; } //当我们通过代理类的对象,调用方法a,就会自动调用如下方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method:就是代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 if(method.getName().equals("eat")){ System.out.println("before rent house"); System.out.println("Method:" + method); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 method.invoke(obj, args); // 在代理真实对象后我们也可以添加一些自己的操作 System.out.println("after rent house");} return null; } } public class proxy { public static void main(String[] args) { SuperMan superMan = new SuperMan(); Human proxyInstance = (Human) ProxyFactory.getProxychInstance(superMan); proxyInstance.getBelief(); proxyInstance.eat("麻辣烫"); } }
运行结果:
before rent house
Method:public abstract void com.lzj.Human.eat(java.lang.String)
我喜欢吃麻辣烫
after rent house
定义好InvocationHandler,这里可以做各种自定义业务;之后生成代理类后便可调用所传入参数的接口interfaces里所有的实现方法了;
之所以需要传入类加载器,是因为在生成代理类的时候,其实内部的原理是通过反射,把我们传入接口里的所有方法都遍历出来,再根据我们自己写好的业务再次编译一个class文件,再通过类加载器加载生成代理类到内存里,这里就很好地反映动态这个词了
CGLIB动态代理:
CGLIB动态代理其实和jdk动态代理很是相似,依旧是在程序运行时生成新的字节码文件,生成被代理类的子类作为代理类,在调用被代理类方法时进行拦截,从而执行我们拦截器里自定义的方法;
同样涉及两个主要类:MethodInterceptor(拦截器接口)、Enhancer(字节码加强器)
package com.lzj; import org.springframework.cglib.proxy.*; import java.lang.reflect.Method; import java.util.Calendar; /** * @Description: TODO * @author: lzj * @date: 2021年04月28日 9:32 */ //目标类 class CglibDao { public CglibDao() { select(); } public void update() { System.out.println("CglibDao.update()"); } public void select() { System.out.println("CglibDao.select()"); } } //拦截器1 class CglibDaoProxy implements MethodInterceptor { public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before method invoke"); methodProxy.invokeSuper(object, args); System.out.println("after method invoke"); return object; } } //拦截器2 class CglibDaoTwoProxy implements MethodInterceptor { public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("startTime : " + Calendar.getInstance().getTimeInMillis()); methodProxy.invokeSuper(object, args); System.out.println("endTime : " + Calendar.getInstance().getTimeInMillis()); return object; } } //回调器 class CglibFilter implements CallbackFilter { public int accept(Method method) { if ("select".equals(method.getName())) { return 1; } return 0; } } public class cglib { public static void main(String[] args) { cglibTest2(); } public static void cglibTest2() { CglibDaoTwoProxy twoProxy = new CglibDaoTwoProxy(); CglibDaoProxy proxy = new CglibDaoProxy(); CglibFilter filter = new CglibFilter(); Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(CglibDao.class); //设置拦截器替代调用方法 enhancer.setCallbacks(new Callback[]{proxy, twoProxy}); //配置回调器,在配置多拦截器时才会用到,根据返回的int作为拦截器数组的下标,采用不同的拦截器 enhancer.setCallbackFilter(filter); //生成代理类 CglibDao dao = (CglibDao) enhancer.create(); dao.update(); dao.select(); } }
执行之后返回的结果:
startTime : 1619592608771
CglibDao.select()
endTime : 1619592608800
before method invoke
CglibDao.update()
after method invoke
startTime : 1619592608800
CglibDao.select()
endTime : 1619592608800
可以看到delect方法对应的是拦截器2;而update方法对应的是拦截器1;
JDK动态代理和cglib代理的区别:
- cglib无需实现接口,达到代理类无侵入,但是被代理类不能是final,因为所创建的代理类其实是被代理类的子类;
- 使用动态代理必须实现接口