jdk动态代理
好处在于我们可以把功能性的代码实现更好的封装,把公共性的代码抽取出来,由代理类去完成。
而被代理实体只注重关键功能的实现,好处在于同一类业务的所有类,都可以交由代理类去代理,我们只需要注重关键代码的开发(例如日志,事务回滚)。
注意:jdk动态代理类是基于接口生成的,所以说我们的被代理实体必须要继承一个接口
原生的jdk实现动态代理的代码实现:
编写被代理类接口
public interface student_interface {
public void add();
public void update();
public void delete();
}
编写被代理实现类
public class studentImp implements student_interface {
@Override
public void add() {
System.out.println("添加学生");
}
@Override
public void update() {
System.out.println("更新学生");
}
@Override
public void delete() {
System.out.println("删除学生");
}
}
编写代理句柄类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyHandle implements InvocationHandler {
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
/**
* 获得动态代理类,由proxy类封装好的方法获得,返回是一个代理实例
*
* @return
*/
public Object getproxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable // 该方法被代理类调用
{
System.out.println("前置操作:Do something");
Object result = method.invoke(target, args); // 执行被代理类相对应的方法,args是参数 method是方法名,即调用target.method(args)
System.out.println("后置操作:Do something");
return result;
}
}
测试类
public class Client {
public static void main(String[] args) {
//1 :创建一个代理句柄
//2 :创建一个被代理实例
ProxyHandle pih = new ProxyHandle(); // 创建代理句柄
//3:将被代理实例向代理句柄注册
pih.setTarget(new studentImp()); // 设置被代理类
//4 :得到代理类
student_interface proxy = (student_interface) pih.getproxy(); // 获取代理类
System.out.println(proxy.getClass().getName());// 返回对象名以$Proxy0结尾
//5 :通过代理类调用相应的方法
proxy.add(); // 会调用pih的invoke方法,invoke方法又调用被代理实体的对应方法
}
}
总结
返回的代理类是一个由jdk的proxy类动态的创建的实例,我们不需要去重复的去为每一个被代理类,写不同的代理实例,相同的业务逻辑只需要写一个代理句柄,通过代理句柄获得代理实例,然后在invoke方法中写你的前置与后置操作
原理
getproxy方法会根据你传入的被代理类,实现的接口动态的生成一个代理类,代理类中所有的方法体其实都是调用 invoke()这个方法。
而invoke方法中你可以自定义你的前置操作和后置操作。所以具有相同业务逻辑的业务执行流程可以封装在被代理类的invoke方法中。
所以说我们同时在内存中创建了三个对象:代理类句柄类(生成代理类,且invoke方法供代理类调用),代理类,被代理类。
spring中的实际应用
spring中AOP和事务控制的原理都是基于动态代理来实现的,spring中动态代理的模式有两种:jdk动态代理与cglib动态代理。
Spring AOP的代理
Spring AOP中的代理使用的默认策略是:
如果目标对象实现了接口,则默认采用JDK动态代理
如果目标对象没有实现接口,则采用CgLib进行动态代理
如果目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理
JDK动态代理和CgLib动态代理的主要区别
- JDK动态代理只能针对实现了接口的类的接口方法进行代理
- CgLib动态代理基于继承来实现代理,所以无法对final类、private方法和static方法实现代理
在于cglib的被代理类不需要继承接口,他可以直接根据类生成代理类。
- aop中我们可以把切点看作是被代理的具体方法,
- 切面就是上述说的前置与后置操作
spring的事务反转
至于spring的事务反转,它的原理是捕获异常时进行rollback,我们可以想象成用try-catch将被代理方法包裹起来,一旦捕获异常则进行数据回滚,并将异常往controller抛出。但是在回滚时默认只会捕获RuntimeException,因为mybatis抛出的异常兼是运行时异常,如果要指定其他异常也回滚的话,注解标签添加@Transactional(rollbackFor = Exception.class)