一、定义
二、分类
(1)静态代理:
(2)动态代理:
三、目的
(1)安全原因:屏蔽第三方直接与真是对象进行通信。
(2)RMI中:需要使用代理类处理远程方法调用的技术细节。
(3)提升系统性能,延迟加载。
四、四个角色
(1)主题接口:定义代理类和真实主题的公共接口
(2)真实主题:真正实现业务逻辑的类
(3)代理类:用来代理和封装真实主题
(4)Main:客户端,第三方,与代理类进行直接通信,完成一些工作。
五、实现
(1)静态代理:
/*
* 静态代理
* 每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理
*/
public class LearnProxy0 {
public static void main(String[] args) {
IDBQuery dbQuery=new DBQueryProxy();
dbQuery.select();
}
}
interface IDBQuery{
public void select();
}
class DBQuery implements IDBQuery{
public DBQuery() {
try {
//可能包含数据库连接等耗时操作,这里仅仅是模拟
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void select() {
// TODO Auto-generated method stub
System.out.println("进行数据库查询操作...");
}
}
class DBQueryProxy implements IDBQuery{
//初始值设置为空,即在软件启动时,不进行初始化操作
private IDBQuery dbQuery=null;
@Override
public void select() {
// TODO Auto-generated method stub
//只有在真正需要的时候,才创建真正的对象
if (dbQuery==null) {
dbQuery=new DBQuery();
}
System.out.println("查询操作前...");
dbQuery.select();
System.out.println("查询操作后...");
}
}
(2)JDK动态代理:
/*
* JDK的动态代理
*/
public class LearnProxy1 {
public static void main(String[] args) {
DynamicProxy proxy=new DynamicProxy();
Sport sportProxy=(Sport)proxy.bind(new Basketball());
sportProxy.play();
}
}
interface Sport{
public void play();
}
class Basketball implements Sport{
public void play() {
System.out.println("i am playing basketball");
}
}
class DynamicProxy implements InvocationHandler{
//要代理的接口,因此动态代理可以代理多个不同接口
private Object target;
public Object bind(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 {
// TODO Auto-generated method stub
System.out.println("方法执行前");
method.invoke(target, args); //通过反射机制调用真实主题的目标方法
System.out.println("方法执行后");
return null;
}
}
(3)cglib动态代理:
/*
* JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了
*
* cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的
* 是继承,所以不能对final修饰的类进行代理
*/
public class LearnProxy2 implements MethodInterceptor{
//
private Object target;
//设置被代理的对象,并且返回一个动态代理
public Object bind(Object target){
this.target=target;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback((Callback) this);
return enhancer.create();
}
@Override
public Object intercept(Object target, Method method, Object[] arg2,
MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("方法执行前...");
proxy.invokeSuper(target, arg2);
System.out.println("方法执行后");
return null;
}
public static void main(String[] args) {
LearnProxy2 proxy=new LearnProxy2();
Actor actorProxy=(Actor)proxy.bind(new Actor());
actorProxy.play();
}
}
class Actor{
public void play() {
System.out.println("表演....");
}
}
使用JDK创建代理有一个限制,即他只能为接口创建代理实例。但是现实情况是,有的类并没有实现接口(现实中存在一些并没有使用接口的项目),但是它也需要动态代理,那怎么办呢?这个时候就需要使用CGLIB啦。
CGLIB采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并且顺势织入横切逻辑。
另外,CGLIB创建的动态代理的性能要比JDK创建的代理的性能好很多。但是,CGLIB创建代理对象花费的时间要比JDK多。因此,我们建议,对于singleton的代理对象,因为不需要频繁创建,最好使用CGLIB技术创建代理,反之,适合使用JDK代理技术。还要注意的是,由于CGLIB采用动态创建子类的方式生成代理对象,所以不能对目标类中的final和private方法进行代理。
下面篇,我会讲解一下动态代理在AOP中的应用。