Spring框架中的JDK与CGLIb动态代理小结

JDK与CGLIb动态代理的区别

废话不多说,直接上例子:

这是例子的简单框架结构;

MathService.java

package com.jd.math;

import org.springframework.stereotype.Service;

@Service
public class MathService implements IMathService {

	@Override
	public int add(int a, int b) {
		return a+b;
	}

	@Override
	public int div(int a, int b) {
		return a/b;
	}
}

MathAOP.java

package com.jd.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MathAOP {

	@Before("execution(public int com.jd.math.MathService.*(..))")
	public void before(JoinPoint jp) {
		Signature signature = jp.getSignature();
		System.out.println("Thw "+signature.getName()+" method begins.");
	}
}

我们先定义一个目标类 MathService,在其中定义两个算法分别为加和除;然后定义一个切面类 MathAOP

配置 application.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<context:component-scan base-package="com.jd"></context:component-scan>
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

 这里我们使用的代理是JDL动态代理

Test.java

package com.jd.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.jd.math.IMathService;
import com.jd.math.MathService;

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		IMathService mathService = applicationContext.getBean(IMathService.class);
		System.out.println(mathService.getClass().getName());  //获取类名
		System.out.println(mathService.getClass().getSuperclass());  //获取父类
		Class clazz = mathService.getClass();  //获取该类实现的接口
		Class [] array = clazz.getInterfaces();
		for(Class c:array) {
			System.out.println("-->"+c);
		}
		applicationContext.close();
	}
}

我们可以看到输出结果如下:

我们发现:JDK动态代理 产生的代理类与目标类没有继承关系;代理类是目标类实现的接口的实现类;

然后我们使用 CGLIb动态代理 看一下;

配置 application.xml 文件(添加属性:proxy-target-class="true";默认为false,即默认为JDK动态代理):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<context:component-scan base-package="com.jd"></context:component-scan>
	<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

同样执行上述的 Test.java 代码,得到的结果如下:

我们发现:CGLIb动态代理 是实现类的子类,继承接口的实现类;因此 Test.java 中获取类可以写实现类,而 JDK动态代理 必须写接口

小结:

  • proxy-target-class="true" CGLIb动态代理,可以使用 IMathService.class 或 MathService.class 获取代理类; proxy-target-class="false"(默认) JDK动态代理,只能使用 IMathService.class 获取代理类;
  • CGLIb动态代理产生的代理类是目标类的子类;
  • JDK动态代理产生的代理类与目标类没有继承关系,代理类是接口的实现类。

注意:Spring 3.2 版本开始支持 CGLIb动态代理。

区别小结:

  1. JDK 动态代理基于接口实现,所以实现 JDK 动态代理,必须先定义接口,CGLIb 动态代理基于被代理类实现;
  2. JDK 动态代理机制是委托机制,委托 hanlder 调用原始实现类方法;CGLIb 则使用继承机制,被代理类和代理类是继承关系,所以代理类对象可以赋值给被代理类类型的变量;如果别代理类有接口,那么代理类对象也可以赋值给该类接口类型的变量。

AOP中隐含的动态代理

一个类中的方法如果被@Transaction注解修饰,则Spring自动为该类生成代理类及代理对象;

先创建一个目标类与目标方法 CarService.java 

package com.jd.car
;
import org.springframework.stereotype.Service;

@Service
public class CarService implements ICarService {

	@Override
	public boolean batch() {
		return true;
	}
}

仅为了证明一个类中的方法如果被@Transaction注解修饰,则Spring自动为该类生成代理类及代理对象;因此不再写方法体,直接返回值;然后配置 application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

	<context:component-scan base-package="com.jd"></context:component-scan>
	
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" p:password="root" p:username="root">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.7:3306/test"></property>
	</bean>

	<bean class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource"></bean>
	
	<!-- 配置数据源事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 开启事务注解 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
	
</beans>

这是对数据库的连接配置和事务开启配置,详解在我的Spring SOP技术小结 博客中;

Test.java

package com.jd.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.jd.car.ICarService;

public class Test {

	public static void main(String[] args) {
		//一个类中的方法如果被@Transaction注解修饰,则Spring自动为该类生成代理类及代理对象
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		ICarService carService = applicationContext.getBean(ICarService.class);
		System.out.println(carService.getClass().getName());
		applicationContext.close();
	}
}

直接获取实例的类,我们会得到如下结果:

是我们定义的目标类,不是代理类;那如果我们将目标类使用@Transaction注解修饰呢?如下:

package com.jd.car;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service  //事务注解修饰
@Transactional
public class CarService implements ICarService {

	@Override
	public boolean batch() {
		return true;
	}
}

再运行 Test.java 代码我们会得到:

JDK动态代理类产生的实例;我们在看一遍结论:一个类中的方法如果被@Transaction注解修饰,则Spring自动为该类生成代理类及代理对象;

上面提到了动态代理有两种,一种是 JDK动态代理,另一种是 CGLIb动态代理,那如何设置两种代理方式呢?

显然上面默认的,什么都不设置的是 JDK动态代理;其实设置 CGLIb动态代理 就是在设置 JDK动态代理 的基础上再在 application.xml 中添加 <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 即可;其中值为 false 即默认为 JDK动态代理,因此可以直接省略该行代码;当添加 <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 代码之后我们再执行 Test.java 可以得到如下结果:

最后再说一下 xml 中的一个标签: import标签;该标签可以引用其他的 xml 文件,使每个 xml 文件变得简洁,如上 application.xml 文件可以写成下面两个文件:

db.xml(数据连接池配置及事务配置)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
	
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" p:password="root" p:username="root">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.7:3306/test"></property>
	</bean>

	<bean class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource"></bean>
	
	<!-- 配置数据源事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 开启事务注解 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

application.xml(实例化类和引上面的 xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<context:component-scan base-package="com.jd"></context:component-scan>
	<import resource="db.xml"/>
	<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
	
</beans>
发布了99 篇原创文章 · 获赞 3 · 访问量 1215

猜你喜欢

转载自blog.csdn.net/qq_44971038/article/details/104267425