静态代理
我们可以使用类的包装来实现静态代理
接口
public interface Flyable {
void fly();
String eat(String food);
}
实现类(目标类)
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird is flying...");
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String eat(String food) {
// TODO Auto-generated method stub
System.out.println("Bird is eating..."+food);
return "eat返回值:吃饱了";
}
}
静态代理类
public class BirdTimeProxy implements Flyable {//在fly方法前后执行一个计时功能
private Flyable flyable;
public BirdTimeProxy(Flyable flyable) {
this.flyable = flyable;
}
@Override
public void fly() {
long start = System.currentTimeMillis();
System.out.println("time-start:"+start);
flyable.fly();
long end = System.currentTimeMillis();
System.out.println("time-over:"+end);
System.out.println("Fly time = " + (end - start));
}
@Override
public String eat(String food) {
return flyable.eat(food);
}
}
可以看出,这里使用了包装的方法
静态代理测试
@Test
public void fun(){
Bird bird = new Bird();
BirdTimeProxy p = new BirdTimeProxy(bird);
p.fly();
p.eat("苹果");
}
结果:
time-start:1590666159624
Bird is flying...................
time-over:1590666160072
Fly time = 448
Bird is eating.................苹果
静态代理的缺点
1.接口中如果有很多个方法,那么这些方法都必须在包装类里面实现,会很麻烦
2.一个代理对象只能代理接口的一个类,如果上述flyable接口有其他的类,比如鸟、昆虫、飞机等等,就需要一个个地去为他们实现代理类,过于麻烦
3.如果接口要增加某个方法,那么不仅实现类要增加方法,相应的代理也要增加方法,就麻烦
动态代理
public static Object newProxyInstance(
ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
主要是使用这个方法来生成动态代理,
第一个参数是类加载器,
第二个参数是要目标类的接口(一个类不止实现一个接口),
第三个参数就是InvocationHandler
的实现类;
其中InvocationHandler 的实现类需要实现的函数就是public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
而这里的Object proxy表示的就是目标类,也就是被代理的类
Method method表示正在执行的方法
Object[] args)表示的是方法传进来的参数
实现一
@Test
public void fun4(){
Flyable flyable=(Flyable) Proxy.newProxyInstance(demo.class.getClassLoader(),
Bird.class.getInterfaces(),
new InvocationHandler() {
//这里生成了InvocationHandler接口的一个匿名类
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//proxy:在这里就没用到,按理说应该是method.invoke(proxy, args);但是下面对应的是new 类
//method:代表正在执行的方法
//args:代表正在执行的方法中的参数
//Object:代表方法执行完毕之后的返回值
System.out.println("正在invoke"+method.getName());
//代表每个方法执行完毕之后返回对象
Object obj=null;
if(method.getName().equalsIgnoreCase("fly")){
System.out.println("起飞前做一些准备");
//打印args中的内容
System.out.println("args:"+Arrays.toString(args));
obj=method.invoke(new Bird(), args);//执行当前的方法
System.out.println("起飞后做一些放松");
}else{
System.out.println("args:"+Arrays.toString(args));
obj=method.invoke(new Bird(), args);//执行当前的方法
}
return obj;
}
});
flyable.fly();
flyable.eat("苹果");
}
测试结果
正在invoke eat
args:[青菜]
Bird is eating.................青菜
正在invoke fly
起飞前做一些准备
args:null
Bird is flying...................
起飞后做一些放松
缺点
可以看到,这样生成的代理,每次代理目标类的方法时,调用 method的public Object invoke(Object obj, Object... args)
都要再new一个目标类,这显然不合理
改进思路
所以可以通过包装的方式,包装类实现InvocationHandler,将目标类作为包装类的一个私有变量,并且重写public Object invoke(Object proxy, Method method, Object[] args)
方法,在这个方法内部调用method的invoke方法时,只要使用一下私有变量就可以啦,其他的和方式一一致。
方式二
public class MyInvocationHandler implements InvocationHandler{
private Object target;
public MyInvocationHandler() {
super();
}
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {//method:代表正在执行的方法
//args:代表正在执行的方法中的参数
//Object:代表方法执行完毕之后的返回值
System.out.println("正在invoke"+method.getName());
//代表每个方法执行完毕之后返回对象
Object obj=null;
if(method.getName().equalsIgnoreCase("fly")){
System.out.println("起飞前做一些准备");
//打印args中的内容
System.out.println("args:"+Arrays.toString(args));
obj=method.invoke(target, args);//执行当前的方法
System.out.println("起飞后做一些放松");
}else{
System.out.println("args:"+Arrays.toString(args));
obj=method.invoke(target, args);//执行当前的方法
}
return obj;
}
}
测试
@Test
public void fun5(){
Flyable target=new Bird();//target类
InvocationHandler invocationHandler=new MyInvocationHandler(target);//改写的
Flyable flyableProxy=(Flyable) Proxy.newProxyInstance(demo.class.getClassLoader(), Bird.class.getInterfaces(), invocationHandler);
flyableProxy.eat("青菜");
flyableProxy.fly();
}
动态代理缺点
被代理的对象必须要实现一些借口,否则无法使用动态代理
cglib代理
spring的包里包含了cglib,就不用再单独导包了
cglib的好处就是可以对所有非final类进行代理,比动态代理少了些限制
首先要Enhancer en=new Enhancer();
然后设置需要代理的目标类en.setSuperclass(Bird.class);
接着要告诉en如何强化方法en.setCallback(callback);
这里的callback
是MethodInterceptor
的实现类,下面的代码则是创建了以一个匿名类。
MethodInterceptor
的实现类中,需要重写public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)
方法,注意,使用目标对象的原生方法时,要用methodProxy.invokeSuper(object, args);
而不是methodProxy.invoke(object, args);
(后者会栈溢出)
@Test
public void fun6(){//cglib代理
Enhancer en=new Enhancer();
en.setSuperclass(Bird.class);
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("打开事务"+method.getName());
Object returnValue=methodProxy.invokeSuper(object, args);
System.out.println("提交事务");
return returnValue;
}
});
Flyable flyable=(Flyable) en.create();
flyable.eat("pear");
flyable.fly();
}
结果
打开事务eat
Bird is eating.................pear
提交事务
打开事务fly
Bird is flying...................
提交事务