框架基础——动态代理
代理
静态代理:
接口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,因为所创建的代理类其实是被代理类的子类;
- 使用动态代理必须实现接口