public interface HelloInterface{
void sayHello();
}
public class Hello implement HelloInterface{
@Override
public void sayHello(){
System.out.println("hello");
}
}
public class HelloProxy implement HelloInterface{
private Hello;
public HelloProxy(Hello hello){
this.hello = hello;
}
@Override
public void sayHello(){
System.out.println("before");
hello.sayHello();
System.out.println("after");
}
}
public class Test{
public static void main(String... args){
Hello hello = new Hello();
HelloProxy proxy = new HelloProxy(hello);
proxy.sayHello();
}
}
invoke(Object proxy, Method method, Object[] args)
proxy:生成的代理对象。
method:接口中的方法。
args:方法参数。
Proxy.newInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler handler)
loader:定义代理类的类加载器,必须能加载接口字节码文件,一般使用被代理对象的类加载器。例如接口为自定义接口,使用根加载器加载,会报"接口加载器不可见"异常。
interfaces:代理类实现的接口,可以为空数组,不能为null,实现被代理类的接口比较有意义。
handler:代理对象关联的调用处理程序,对代理对象实例方法的调用都是通过InvocationHandler中的invoke方法来完成的,而invoke方法会根据传入的代理对象、方法名称以及参数决定调用代理的哪个方法。
public interface HelloInterface{
void sayHello();
}
public class Hello implement HelloInterface{
@Override
public void sayHello(){
System.out.println("hello");
}
}
public class HelloHandler implement InvocationHandler{
private Object target;
public HelloHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object obj = method.invoke(target, args);
System.out.println("after");
return obj;
}
}
public class Test{
public static void main(String... args){
Hello hello = new Hello();
HelloHandler h = new HelloHandler(hello);
HelloInterface proxy = (HelloInterface)Proxy.newInstance(Test.class.getClassLoader,Hello.class.getInterfaces(),h);
proxy.sayHello();
}
}
intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
obj:被代理对象
method:被拦截的方法
args:方法参数
proxy:代理方法
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。
public class Hello{
public void sayHello(){
System.out.println("hello");
}
}
public class MyMethodInterceptor implement MethodInterceptor{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before");
proxy.invokeSuper(obj, args);//执行被代理对象的原有方法
System.out.println("after");
return null;
}
}
public class Test{
public static void main(String... args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperClass(Hello.class);//为代理类设置父类
enhancer.setCallback(new MyMethodInterceptor());//设置回调方法,类似于JDK动态代理的InvocationHandler
Hello proxy = (Hello)enhancer.create();
proxy.sayHello();
}
1、Spring会为容器中的对象生成一个jdk或者cglib的动态代理对象,并缓存到对象工厂中,当其他类需要注入某些类时,实际注入的是代理对象。
2、Spring中默认使用的是jdk动态代理,可以通过spring.aop.proxy-target-class属性设置代理方式,true为cglib代理,false为jdk代理。
1、Spring容器在初始化每个单例bean的时候,会遍历容器中的所有BeanPostProcessor实现类,并执行其postProcessAfterInitialization方法。这一过程中会遍历容器中所有的切面,并查找与当前实例化bean匹配的切面,如果切面存在就为Bean创建代理JDK或者Cglib对象,@Transactional注解属于事务属性切面。
参考Spring源码中AbstractAutoProxyCreator类的postProcessAfterInitialization和wrapIfNecessary方法,wrapIfNecessary是创建代理对象的核心方法。
2、执行service的事务方法时,执行的是代理对象经过增强的方法。首先获取方法的拦截器链,如果不存在就直接执行代码中的service方法。存在事务注解,则会得到TransactionInterceptor,执行其中的invoke方法。
参考Spring源码中JdkDynamicAopProxy.invoke()、CglibAopProxy.DynamicAdvisedInterceptor.intercept(),以及TransactionInterceptor.invoke()方法。
3、依次开启事务、执行目标方法、提交事务,发生异常事务回滚。
参考Spring源码中TransactionAspectSupport类的createTransactionIfNecessary,cleanupTransactionInfo,commitTransactionAfterReturning以及completeTransactionAfterThrowing方法。
1、@Transactional注解只有在public方法上才有效。JDK代理对象是被代理类接口的实现类,接口类都是public;cglib代理对象继承自被代理对象,理论上public和protected修饰的方法都可以被代理,但是Spring中代理时遍历的是public方法。
2、@Transactional默认遇到非受查异常(RuntimeException和Error)回滚,发生受查异常时不回滚。可以设置rollBackFor和noRollbackFor属性定义事务回滚的异常类型。
3、同一个Service中方法相互调用,如A调用B,则是否存在事务取决于A是否存在事务注解。原因是,在使用代理对象调用A方法时,只会根据A的注解来确定是否开启事务,A中调用B调用的是原生的B方法。
可以以另外的方法实现情形下的事务:
ServiceImpl bean = applicationContext.getBean(this.getClass());
从容器中获取对象,bean是被cglib增强的对象。通过对象执行加了事务注解的方法,此时,A主方法的事务和B方法的事务是独立的。
4、不同的Service方法相互调用,如S1.A()调用S2.B(),S1和S2都是被代理的对象,会分别根据方法是否含有事务注解来开启事务。具体处于哪一个事务,取决于事务的传播行为,即Propagation属性,默认是PROPAGATION_REQUIRED。
5、在主线程中开子线程,各个线程之间事务独立。因为事务信息是利用ThreadLocal和当前线程绑定的,保证在同一个线程的不同方法中操作数据库使用同一个连接。新开线程之后,子线程没法共享主线程的事务信息。参考TransactionAspectSupport.bindToThread()方法。