1.0 在开始之前
需要知道代理是什么?
代理已经成为一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
代理分为两种,静态代理和动态代理
静态代理方式需要代理对象和目标对象实现一样的接口,优点:可以在不修改目标对象的前提下扩展目标对象的功能。缺点:1)冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。2)不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
而常用的动态代理则是:动态地在内存中基于目标对象构建代理对象,从而实现对目标对象的代理功能。
在Java世界中,动态代理常见的实现方案有:JDK动态代理与CGLib动态代理。
静态代理与动态代理的区别主要在:
- 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件
动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中。
JDK动态代理
JDK动态代理需要目标类实现某个接口,所以也称为接口代理或JDK代理,通过JDK提供的
InvocationHandler与Proxy
来动态的创建代理实现,一般其编程用法如下: ```java 1) 定义接口 public interface ServiceBean {String doService(String param); }
2)实现接口 public class ServiceBeanImpl implements ServiceBean{
@Override
public String doService(String param) {
return "Hello," + param;
}
}
3)自定义代理行为,实现InvocationHandler private static class MyInvokationHandler implements InvocationHandler { private Object target; MyInvokationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(“JDK生成的代码类: “ + proxy.getClass().getName()); System.out.println(“调用方法: “ + method.getName()); System.out.println(“调用参数: “ + ArrayUtils.toString(args)); Object invoked = method.invoke(target, args); System.out.println(“后置处理…”); return invoked; }
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), new MyInvokationHandler(target));
}
}
4) 创建一个目标对象, 运行代理行为 public static void main(String[] args) throws Throwable { ServiceBean impl = new ServiceBeanImpl(); //目标类 MyInvokationHandler handler = new MyInvokationHandler(impl); ServiceBean bean = (ServiceBean)handler.getProxy(); //获取代理类 String returnVal = bean.doService(“JDK Proxy”); //在代理对象上调用目标方法 System.out.println(returnVal); }
<a name="ym1ps"></a>
### CGLIB代理
[cglib](https://github.com/cglib/cglib) (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。<br />**cglib特点**
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。<br />如果想代理没有实现接口的类,就可以使用CGLIB实现。
- CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。<br />它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
- CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。<br />不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。
cglib与动态代理最大的**区别**就是
- 使用动态代理的对象必须实现一个或多个接口
- 使用cglib代理的对象则无需实现接口,达到代理类无侵入。
使用cglib需要引入[cglib的jar包](https://repo1.maven.org/maven2/cglib/cglib/3.2.5/cglib-3.2.5.jar),如果你已经有spring-core的jar包,则无需引入,因为spring已经内置了CGlib代码,如果是非Spring环境, 则对应的Maven坐标:
```xml
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>版本</version>
</dependency>
使用cglib来实现代理的一般步骤为:
- 实现org.springframework.cglib.proxy.MethodInterceptor接口,自定义拦截逻辑
- 通过Enhancer来生成CGLIB代理
- 调用目标方法
简要示例如下:
// 定义一个目标对象,可以是任何class, 不需要实现接口
public static class Foo {
public String bar(String param) {
return "Hello, " + param;
}
}
public static void main(String[] args) {
Enhancer en = new Enhancer();
// 设置要代理的目标类
en.setSuperclass(Foo.class);
// 设置要代理的拦截器
en.setCallback(new MyInterceptor());
// 生成代理类的实例
Foo proxy = (Foo) en.create();
String response = proxy.bar("Cglib");
System.out.println(response);
}
public static class MyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before Execution, check something ...");
// 执行目标方法,并保存目标方法执行后的返回值
Object returnVal = methodProxy.invokeSuper(o, objects);
System.out.println("After Execution, finalize something ...");
return "Intercepted " + returnVal;
}
}
2.0 需要清楚Spring Bean的生命周期
无论是Spring项目还是SpringBoot项目,Spring通过注解扫描或XML配置扫描把所有的Bean都会载入容器中,成为Bean定义对象,然后执行Bean的初始化,以及Bean的依赖注入。在依赖注入过程递归的创建Bean。所有Bean的创建流程遵循相同的流程,其生命周期如下:
所以每个Bean都会执行BeanPostProcessor。
而Spring一方面内置了很多默认的BeanPostProcessor, 另一方面根据项目的配置,会动态的注册很多BeanPostProcessor, 以SpringBoot项目为例,开启事物管理的示例代码如下
@SpringBootApplication
@EnableTransactionManagement
public class ManagingTransactionsApplication {
public static void main(String[] args) {
SpringApplication.run(ManagingTransactionsApplication.class, args);
}
}
启动该main方法时,执行SpringBoot项目的初始化流程,通过自动配置扫描机制会自动载入很多Bean到容器中。当标记@EnableTransactionManagement注解后,会装配一个类型为InfrastructureAdvisorAutoProxyCreator
的BeanPostProcessor. 每个Bean都会执行该processor的postProcessAfterInitialization
方法,在该方法中完成Bean的包装,如果满足创建代理对象的条件,则需要为该Bean创建动态代理。其代理方式分为JDK代理与CGLib代理,基于其是否实现接口来创建对应的代理类型(见上文)
�
3.0 @Transactional的使用方式
@Transactional 可以标注到Class上,也可以标注到Method上。如果是标注到Method上,需要是Public类型的非final方法(底层实现是动态代理,非public方法或final修饰的方法不能被重写),用法简要如下:
@Component
@Transactional
public class BookingService {
private final static Logger logger = LoggerFactory.getLogger(BookingService.class);
private final JdbcTemplate jdbcTemplate;
public BookingService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Transactional
public void book(String... persons) {
for (String person : persons) {
logger.info("Booking " + person + " in a seat...");
jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
}
}
如果修饰到Class上,则该Class的所有非final的public方法都会有事物功能,如果只修饰某个方法,则该方法才有事物功能。
4.0 @Transactional有什么作用
a. 为Bean创建代理
在初始化某Bean的时候,如上文的BookService, 执行InfrastructureAdvisorAutoProxyCreator
的初始化逻辑如下:
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 查找当前Bean的Class是否有切面拦截器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// 如果有则为该Bean创建一个代理,创建方式底层分为JDK代理或CGlib代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
b. Bean的Class需要哪些切面拦截器
1)Spring会查找所有的切面Advisor
,Advisor
内部封装AOP的拦截器(Advice)与切点(PointCut)
�可以看到,开启了事物管理的应用,生效的Advisor是:BeanFactoryTransactionAttributeSourceAdvisor
, 执行findAdvisorsThatCanApply方法是,则检查当前Bean的Class(BookService)的任意一个Method是否包含
�@Transactional注解,如果包含则BeanFactoryTransactionAttributeSourceAdvisor
切面可以作用到
�当前Bean上。
可以看到BeanFactoryTransactionAttributeSourceAdvisor
的advice是:org.springframework.transaction.interceptor.TransactionInterceptor,
也就是说,代理对象的代理行为会通过TransactionInterceptor来定义。
c. TransactionInterceptor 是如何被载入的?
是因为开启了EnableTransactionManagement,默认会载入ProxyTransactionManagementConfiguration
,该配置类定义如下:
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
// 定义Adisor
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
// 定义TransactionInterceptor
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}
}
�为什么会载入ProxyTransactionManagementConfiguration
,是因为EnableTransactionManagement注解配置了@Import(TransactionManagementConfigurationSelector.class), 而TransactionManagementConfigurationSelector的实现如下:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* Returns {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJ(Jta)TransactionManagementConfiguration} for {@code PROXY}
* and {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()},
* respectively.
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
// 导入ProxyTransactionManagementConfiguration
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
d. TransactionInterceptor如何执行事务代理行为
实现代码见:TransactionAspectSupport#invokeWithinTransaction,不过其本质就是执行如下代码块:
1.获取数据库连接
2.设置autoCommit=0
3.开启事物(begin)
try {
4. 执行被代理对象的业务行为(内部封装了一个InvocationCallback)
catch(Throwable e) {
5. 被代理对象的业务方法抛出异常,执行回滚rollback
} finally {
6. 被代理对象的业务方法未抛出异常,执行提交commit
}
5.0 总结
如果某个Bean对应的Class上有标注@Transactional或非final的public方法上标注了Transactional注解,Spring框架通过会为该Bean创建动态代理,如果Bean的Class实现了某接口,依赖注入的也是该Bean对应的接口,底层会创建一个JDK代理。如果Bean的Class未实现接口或强制开启了CGLIB代理(proxyTargetClass=true),依赖注入时之前注入的Bean对应的Class,底层会创建一个CGLIB代理对象。
但是无论是JDK代理还是CGLib代理,都是统一通过AOP 联盟API提供的方法拦截器API:TransactionInterceptor来代理其具体的行为,在原有目标对象的行为上增强事物的流程处理。
1)Bean的代理实现:实现了某接口,注入的也是接口
@Component
public class BookingService implements IBookingService {
...
}
2)Bean的代理实现:未实现接口, 注入的Class
@Component
public class BookingService {
...
}
可以看到,2中代理策略实现都是统一封装了ProxyFactory对象。而ProxyFactory则封装了目标对象(targetSource), 具体的切面拦截器(advisors), 每个切面有自己的拦截行为(advice).