不想让某个对象直接调用另一个对象的时候,可以通过代理对象来调用。spring aop中声明式事务,用到代理模式,把dao方法的事务开启和提交从dao类分离出去,让代理对象为之添加事务的操作,如此我们便关注dao的方法即可无需关注事务。这样耦合性降低,方便维护。代理对象分为静态代理和动态代理2中。
静态代理:在程序运行前写好的代理类。
动态代理:在程序运行时,动态创建。
1.静态代理
现在对studentDao接口的查询方法(selectStudent)地调用,为它加上模拟事务的开始事务和提交事务方法selectStudent。
StudentDao
package com.lg.proxy.staticproxy;
public interface StudentDao {
void selectStudent();
}
StudentDaoImpl
package com.lg.proxy.staticproxy;
public class StudentDaoImpl implements StudentDao {
@Override
public void selectStudent() {
System.out.println("selectStudent");
}
}
Transaction
package com.lg.proxy.staticproxy;
public class Transaction {
public void beginTransaction() {
System.out.println("beginTransaction");
}
public void commit() {
System.out.println("endTransaction");
}
}
StudentProxy
package com.lg.proxy.staticproxy;
public class StudentProxy implements StudentDao{
private StudentDao studentDao;
private Transaction transaction;
@Override
public void selectStudent() {
transaction.beginTransaction();
studentDao.selectStudent();
transaction.commit();
}
public StudentProxy(StudentDao studentDao, Transaction transaction) {
super();
this.studentDao = studentDao;
this.transaction = transaction;
}
}
测试结果:
从静态类固定地写好的代码来看,这样是不方便维护的,如果接口做了修改而且dao类和方法很多的时候修改起来是非常话费时间的。所以使用动态代理会比静态代理好。
2.动态代理:jdk代理
jdk动态代理需要实现支持java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy
在上面的基础上增加一个StudentInterceptor
package com.lg.proxy.dynamic.jdkproxy;
import java.lang.reflect.Method;
public class StudentInterceptor implements java.lang.reflect.InvocationHandler{
private Object target;
public StudentInterceptor(Object target, Transaction transaction) {
super();
this.target = target;
this.transaction = transaction;
}
private Transaction transaction;
/**
* Method 目标类被调用的方法 args被调用方法的入参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
transaction.beginTransaction();
method.invoke(target, args);
transaction.commit();
return null;
}
}
测试类ProxyTest
package com.lg.proxy.dynamic.jdkproxy;
import static org.junit.jupiter.api.Assertions.*;
import java.lang.reflect.Proxy;
import org.junit.jupiter.api.Test;
class ProxyTest {
@Test
public void testProxy() {
StudentDao dao=new StudentDaoImpl();
Transaction transaction=new Transaction();
StudentInterceptor interceptor=new StudentInterceptor(dao, transaction);
//创建代理对象 Proxy.newProxyInstance参数分别是:
//1.目标类的加载器 2.目标类的全部的接口 3.InvocationHandler接口的实例
StudentDao proxy=(StudentDao) Proxy.newProxyInstance(
dao.getClass().getClassLoader(),
dao.getClass().getInterfaces(),
interceptor);
proxy.selectStudent();
}
}
3.动态代理:cglibproxy
cglib代理类
studentInterceptor
package com.lg.proxy.dynamic.cglibProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class StudentInterceptor implements MethodInterceptor{
private Object target;
public StudentInterceptor(Object target, Transaction transaction) {
super();
this.target = target;
this.transaction = transaction;
}
private Transaction transaction;
@Override
public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable {
transaction.beginTransaction();
Object rt=method.invoke(target, arg2);
transaction.commit();
return rt;
}
//创建代理对象
public Object createProxy() {
Enhancer enhancer=new Enhancer();//代码增强类
enhancer.setCallback(this);//参数是拦截器
enhancer.setSuperclass(target.getClass());//设置生成代理类的父类为目标类
return enhancer.create();
}
}
4.jdk代理和cglib代理的区别
1.JDK动态代理只能针对实现了接口的类生成代理。
2.CGLIB代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的。
在spring中如果目标对象没有实现接口,则默认会采用CGLIB代理;
如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。