比如A是一个接口,B是被加强对象,C是代理对象
A接口有一个say()方法,B实现了A接口,C也实现了A接口,C在自己实现的say()方法里不但执行了B的say()方法也执行了一些自己的程序,这就是代理。
代理分为静态代理和动态代理。
类的增强可以通过继承来实现,那如果想让某个方法增强呢?这时候就用到代理了。
大体上有目标类,代理类,增强(通知),连接点。
代理类在连接点处通过增强(通知)来对目标类进行增强。
我简单理解成增强就是在目标执行某方法进行拦截时执行的代码,拦截就是对方法或者参数进行分析,具体拦截后怎么做自己想怎么写就怎么写。
连接点是指那些被拦截到的点,大白话就是在哪些方法中拦截,总不能所有目标类的方法都拦截吧,只拦截我想要增强的方法就行了,在Spring中这些点指的是方法因为Spring只支持方法类型的连接点
控制真实对象的访问权限 通过代理对象控制对真实对象的使用权限。
避免创建大对象 通过使用一个代理小对象来代表一个真实的大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
增强真实对象的功能 这个比较简单,通过代理可以在调用真实对象的方法的前后增加额外功能。
1、代理对象和目标对象都要实现的 公共接口
2、代理对象持有目标对象引用,重写构造方法,
3、实现共同的方法,代理对象增强行为。
缺点:
代理角色固定,一次只能代理一个对象。
接口
public interface Marry {
void marry();
}
public class You implements Marry {
public void marry() {
System.out.println("sihai get marry");
}
}
public class MarryCompany implements Marry {
private You you;
public MarryCompany(You you) {
this.you = you;
}
public void marry() {
before();
you.marry();
after();
}
private void after() {
System.out.println("get after work done");
}
private void before() {
System.out.println("get ready for marry");
}
}
public class TestMarry {
public static void main(String[] args) {
You you = new You();
MarryCompany marryCompany = new MarryCompany(you);
marryCompany.marry();
}
}
get ready for marry
sihai get marry
get after work done
那么多目标,一个一个写是不是太浪费时间了?
这时有两种解决方案:
硬编码是说写死的编码,增强的方法都写死固然会很繁琐
实现JDK的动态代理有两部分最重要:
1.动态代理对象的生成(需要类加载器,实现接口类型,调用处理程序,源码这样写的)
2.调用处理程序(执行的增强方法)
举个栗子:
接口类
public interface DogI {
void say();
}
目标类
public class Dog implements DogI{
@Override
public void say() {
System.out.println("hello");
}
}
动态生成代理对象的类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DogJDKPROXY implements InvocationHandler {
//target为传进来的目标类
private Object target;
//获取传进来的目标类,并获取目标类的 类加载器,类接口,this指的是调用此方法的目标(还是目标类),需要实现InvocationHandler接口,重写invoke方法并执行
public Object getProxyObject(Object target) {
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//在这里可以进行拦截,加强之类的操作,我这里什么增强都没写
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理的类,执行的方法,传递的参数
Object result = method.invoke(target, args);
return result;
}
}
测试
public class Test {
public static void main(String[] args) {
DogI dog =(DogI) new DogJDKPROXY().getProxyObject(new Dog());
dog.say();
}
}
JDK的动态代理是生成
目标类
的
接口对象
,是接口对象!
目标类
public class Person {
public void say() {
System.out.println("hello");
}
}
动态生成代理对象的类
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class PersonCGLIBProxy implements MethodInterceptor{
//target为传过来的目标类
private Object target;
//获取目标类,需要Enhancer对象获取目标类的,然后从字节码的级别创造代理对象,设置setCallback才能拦截(增强),把目标类放进去实现MethodInterceptor并重写intercept
public Object getProxyObject(Object target) {
this.target=target;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
//这个方法就是增强的地方,arg0为生成的代理对象,arg1传进来的需要增强的方法(这里默认目标类的方法都拦截了)
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy methodProxy) throws Throwable {
Object result=methodProxy.invoke(target, arg2);
return result;
}
}
测试
public class Test {
public static void main(String[] args) {
Person p = (Person) new PersonCGLIBProxy().getProxyObject(new Person());
p.say();
}
}
对比一下两者发现非常相似,都是传过来一个目标类,然后获取这个目标类的某些东西,然后创建一个代理类,如果代理类执行方法,就会通过一个固定的方法来获取这个方法以及这个方法的参数,然后在代理类的这个方法里进行一些处理。
但是:使用动态代理机制虽然解决了代理对象的生成问题,但是要为对象的哪个方法增强,以及增强应该如何实现等等,没有实现这样的功能,所以非常不灵活
为了解决这个方法,可以使用Spring的AOP解决。
Java的动态代理,在日常开发中可能并不经常使用,但是并不代表他不重要。Java的动态代理的最主要的用途就是应用在各种框架中。因为使用动态代理可以很方便的运行期生成代理类,通过代理类可以做很多事情,比如AOP,比如过滤器、拦截器等。
在我们平时使用的框架中,像servlet的filter、包括spring提供的aop以及struts2的拦截器都使用了动态代理功能。我们日常看到的mybatis分页插件,以及日志拦截、事务拦截、权限拦截这些几乎全部由动态代理的身影。