代理模式:
作用:为目标对象提供一个代理对象以控制对目标对象的访问。
说明:代理对象存在的价值:主要用于拦截对真实业务对象(目标对象)的访问
应用:拦截器等
静态代理:
代理类和目标类必须实现相同的接口或者是继承相同父类。
静态代理模式的缺点:
1,如果一个系统中有100个目标(被代理)对象,则要创建100个代理对象
2,如果一个目标对象中有很多方法需要事务,则代理对象的很多方法中都需要写事务相关的代码,重复代码比较多
3,当我们在目标类中增加了一个方法时,代理类中也要增加相应方法。
3,总结:静态代理中,代理对象的重用性不强
***代理模式与装饰模式的比较:
1)在装饰模式中,装饰实现类和被装饰的类实现了共同的原始接口;在代理模式中,代理类和目标类实现了相同的接口或继承了相同的父类
2)装饰模式是给被装饰的类对象添加一个或多个功能(即增强功能);代理模式是对代理对象的使用加以控制,并不提供对象本身的增强功能。
eg:
// 实现了UserDao接口的目标类
public class UserDaoImpl implements UserDao{
@Override
public void deleteUser() {
System.out.println("delete user");
}
}
// 实现了UserDao接口的代理类
public class UserDaoImplProxy implements UserDao {
// 代理的目标对象
private UserDao userDao;
public UserDaoImplProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void deleteUser() {
System.out.println("== 开始事务 ==");
userDao.deleteUser();
System.out.println("== 提交事务 ==");
}
}
// 测试类
public class ProxyTest {
@Test
public void test() {
// 创建目标对象
UserDao userDaoImpl = new UserDaoImpl();
// 创建代理对象
UserDao userDaoImplProxy = new UserDaoImplProxy(userDaoImpl);
// 执行方法(使用的是代理对象)
userDaoImplProxy.deleteUser(); // 使用代理对象
}
}
console:
== 开始事务 ==
delete user
== 提交事务 ==
动态代理:
1,代理对象和目标对象实现了共同的接口
jdk动态代理($Proxy):
1)JVM可以在运行期间动态生成类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
2)JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理
3)Java提供了一个java.lang.reflect.Proxy类,调用它的newProxyInstance方法可以生成目标对象的代理对象
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
参数:
loader - 定义代理类的类加载器,一般采用跟目标对象相同的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
返回:一个带有指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
4)每个代理实例都具有一个关联的调用处理程序,这个调用处理程序需要实现InvocationHandler接口。代理对象调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke方法。
java.lang.reflect包下的InvocationHandler接口中的invoke方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}
参数:
proxy - 代理对象
method - 代理对象调用的(接口)方法的 Method 实例。
args - 代理对象调用的(接口)方法的参数值的对象数组
返回:方法的返回值,方法没有返回值(void)时返回null
jdk动态代理总结:
1)jdk动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口这两个来完成的。
2)要使用JDK动态代理,必须要定义接口。
3)JDK动态代理将会拦截所有pubic的方法(因为只能调用接口中定义的方法),这样即使在接口中增加了新的方法,其他地方不用修改,新的方法也会被拦截。
4)如果只想拦截一部分方法,可以在invoke方法中对要执行的方法名进行判断。
eg:
// 实现了UserDao接口的目标类
public class UserDaoImpl implements UserDao{
public void deleteUser() {
System.out.println("delete User");
}
public void saveUser() {
System.out.println("save User");
}
}
// 调用处理程序:实现了InvocationHandler(调用处理器)的拦截器
public class TransactionInterceptor implements InvocationHandler{
private UserDao target;//代理的目标对象
private Transaction transaction;//添加事务
public TransactionInterceptor(UserDao target, Transaction transaction){
this.target = target;
this.transaction = transaction;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.transaction.beginTransaction();
Object result = method.invoke(this.target, args);//调用目标类的方法
this.transaction.commit();
return result;
}
}
// 提供事务操作的类
public class Transaction {
public void beginTransaction(){
System.out.println("开启事务 ");
}
public void commit(){
System.out.println("事务提交");
}
}
// 测试类
public class JdkProxyTest {
@Test
public void test(){
// 创建目标对象
UserDao target = new UserDaoImpl();
// 创建调用处理器
TransactionInterceptor transactionInterceptor = new TransactionInterceptor(target, new Transaction());
// 用Proxy创建jdk动态代理对象
UserDao userDao = (UserDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), transactionInterceptor);
// 执行方法(使用的是代理对象)
userDao.deleteUser();
}
}
Console:
开启事务
delete user
事务提交
2,代理对象是目标对象的子类
hibernate:Person person = session.load(Person.class,1L); javassisit
cglib
cglib动态代理:
1)JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
2)CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。
3)CGLIB动态代理是使用net.sf.cglib.proxy.MethodInterceptor接口完成的。
MethodInterceptor接口中的intercept方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {}
参数:
proxy - 代理对象
method - 代理对象调用的(父类)方法的 Method 实例。
args - 代理对象调用的(父类)方法的参数值的对象数组
methodProxy - 使用它调用(父类)的方法
返回:方法的返回值,方法没有返回值(void)时返回null
CGLIB代理总结:
1)CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
2)要求类不能是final的,要拦截的方法要是非final、非static、非private的。
// 目标类:此类不能是final的,否则不能有子类,CGLIB也就不能工作了
public class UserDaoImpl implements UserDao{
// 注:这个方法不能被final、static、private任何一个修饰,否则CGLIB不会拦截这个方法
public void deleteUser() {
System.out.println("delete User");
}
public void saveUser() {
System.out.println("save User");
}
}
public class TransactionInterceptor implements MethodInterceptor{
private UserDao target;//代理的目标对象
private Transaction transaction;//添加事务
public TransactionInterceptor(UserDao target, Transaction transaction){
this.target = target;
this.transaction = transaction;
}
// 创建代理对象
public UserDaoImpl createProxyInstance() {
Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象
enhancer.setSuperclass(this.target.getClass()); // 设置父类
enhancer.setCallback(this); // 设置回调对象(注:实现了MethodInterceptor接口的对象可以作为回调对象)
return (UserDaoImpl) enhancer.create(); // 创建代理对象并返回
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
this.transaction.beginTransaction();
Object result = methodProxy.invoke(this.target, args);
this.transaction.commit();
return result;
}
}
// 测试类
public class CglibProxyTest {
@Test
public void test(){
// 创建目标对象
UserDao target = new UserDaoImpl();
// 创建方法拦截器
TransactionInterceptor transactionInterceptor = new TransactionInterceptor(target, new Transaction());
// cglib动态代理对象
UserDaoImpl userDaoImpl = (UserDaoImpl)transactionInterceptor.createProxyInstance();
// 执行方法(使用的是代理对象)
UserDaoImpl.deleteUser();
}
}
Console:
开启事务
delete user
事务提交
3,jdk动态代理和cglib动态代理的比较:
jdk动态代理的优点: 因为有接口,所有使系统更加低耦合,缺点是必须要有接口存在
cglib动态代理的特点:不需要接口的存在,故耦合性方面没有jdk动态代理好
Spring的动态代理:
spring在运行期创建代理,不需要特殊的编译器。spring有两种代理方式:
1,若目标对象实现了若干接口,spring就会使用jdk动态代理(默认)
2,若目标对象没有实现任何接口,spring就使用cglib库生成目标对象的子类。
代理模式--静态代理VS动态代理
最新推荐文章于 2024-09-10 16:35:51 发布