一,概述
1)代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理访问目标对象.
2)代理的好处:可以在目标对象实现的基础上,增加额外的功能操作,即扩展目标对象的功能.
3)分类:静态代理,动态代理,cglib代理.
4)代理模式的关键点:代理对象与目标对象.
5)举例:商家是直接联系不到明星的,一般情况下,都是商家找明星经纪人,然后明星经纪人通知明星.明星是目标对象,明星经纪人就是代理对象.
二,静态代理
1)要点:代理对象要实现与目标对象一样的接口.
2)举例:模拟保存用户
UserDao-->直接保存
UserDaoProxy-->给保存方法添加事务管理
3)接口:IUserDao.java
public interface IUserDao {
void save();
}
4)目标对象:UserDao.java,实现了IUserDao接口
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("***保存用户数据到数据库");
}
}
5)代理对象:UserDaoProxy.java,与目标对象一样实现了IUserDao接口
public class UserDaoProxy implements IUserDao{
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("开启事务...");
//执行目标对象的方法
target.save();
System.out.println("提交事务...");
}
}
6)测试:
public class App {
@Test
public void testProxy() throws Exception {
IUserDao userDao=new UserDao();
UserDaoProxy proxy=new UserDaoProxy(userDao);
proxy.save();
}
}
结果:
开启事务...
***保存用户数据到数据库
提交事务...
综上,扩展了UserDao的save接口.
7)总结静态代理:
a)优点:可以做到在不修改目标对象的前提下,对目标对象的功能进行扩展.
b)缺点:因为代理对象需要与目标对象实现一样的接口,所以会造成很多代理类,类太多;一旦接口增加方法,目标对象与代理对象都要维护.(动态代理就可以避免这些问题,借助代理工厂完成)
三,动态代理
1)代理对象不需要实现接口.
2)代理对象的生成利用了JDK API,动态地在内存中构建代理对象(需要我们指定创建代理对象 目标对象 实现的接口的类型)
3)动态代理也叫JDK代理,或者接口代理.
4)接口和目标对象还是用上面代码,代理工厂代码如下:
package com.bighuan.b_dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 给所有Dao创建代理对象(动态代理)
*
* @author bighuan
*
*/
public class ProxyFactory {
// 维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),// 得到目标对象实现的接口类型
// new Class[]{IUserDao.class}
new InvocationHandler() {// 事件处理器
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// 开启事务
System.out.println("***开启事务***");
String name = method.getName();// 拿到方法名
System.out.println(name);
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
// 提交事务
System.out.println("***提交事务***");
return returnValue;
}
});
}
}
5)测试:
@Test
public void testProxy() throws Exception {
//目标对象
IUserDao userDao=new UserDao();
//原始类型:class com.bighuan.b_dynamic.UserDao
System.out.println(userDao.getClass());
//给目标对象创建代理对象
IUserDao proxy=(IUserDao) new ProxyFactory(userDao).getProxyInstance();
//class $Proxy4,内存中动态生成的代理对象
System.out.println(proxy.getClass());
//执行方法[代理对象]
proxy.save();
}
效果和上面的静态代理一致,但却规避了静态代理的缺点.
6)动态代理总结:代理对象不需要实现接口,但是目标对象一定要实现接口;否则不能使用动态代理!
如果一个目标对象需要实现功能扩展,但是目标对象却没有实现接口,该怎样实现功能扩展呢?
四,Cglib代理
1)Cglib代理也叫子类代理,在内存中构建一个子类对象从而实现对目标对象的功能扩展.
2)JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口.如果想代理没有实现接口的类,就可以使用CGLIB实现.
3)CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截).
4)CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
5)开发准备:需要引入Cglib的相关jar文件,但是Spring的核心包中已经包括了cglib功能,所以自接导入spring-core-3.2.5.jar即可;引入功能包后,就可以在内存中动态构建子类.
6)代理类不能为final类(final类没有子类),目标对象的方法如果为final或static(final或static修饰的方法,子类不能重写),那么就不会拦截,即不会执行除目标对象之外的业务方法.
7)UserDao.java
public class UserDao {
public void save() {
System.out.println("***保存用户数据到数据库");
}
}
ProxyFactory.java
package com.bighuan.c_cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* Cglib子类代理工厂
* (对UserDao,在内存中动态构建一个子类对象)
* @author bighuan
*
*/
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象创建代理对象
public Object getProxyInstance(){
//1,工具类
Enhancer hancer=new Enhancer();
//2,设置父类
hancer.setSuperclass(target.getClass());
//3,设置回调函数
hancer.setCallback(this);
//4,创建子类(代理对象)
return hancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("**********开启事务*****");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("**********提交事务*****");
return returnValue;
}
}
8)测试:
@Test
public void testProxy() throws Exception {
//目标对象
UserDao target=new UserDao();
//class com.bighuan.c_cglib.UserDao
System.out.println(target.getClass());
//代理对象
UserDao proxy=(UserDao) new ProxyFactory(target).getProxyInstance();
//class com.bighuan.c_cglib.UserDao$$EnhancerByCGLIB$$b6d37fe1
System.out.println(proxy.getClass());
//执行代理对象的方法
proxy.save();
}
结果:
**********开启事务*****
***保存用户数据到数据库
**********提交事务*****
五,总结
下一篇,Spring的AOP编程,期待吧,boys!