一 概述
1 分类
静态代理:在程序运行前就已经存在代理类的字节码文件,代理对象和真实对象的关系在运行前就确定了。(代理类及对象要自行创建)
动态代理:代理类是在程序运行期间由 JVM 通过反射等机制动态的生成的,不存在代理类的字节码文件,动态生成字节码对象,代理对象和真实对象的关系是在程序运行时期才确定的。(代理类及对象不需要自行创建)
2 动态代理实现方式
真实类有接口使用 JDK 动态代理;
真实类没实现接口使用 CGLIB 或 Javassist 组件。
二 静态代理
1 介绍
静态代理是一种代理模式,其中代理对象在编译时就确定下来,而不是在运行时动态创建。静态代理由业务实现类、业务代理类两部分组成。业务实现类负责实现主要的业务方法,业务代理类负责对调用的业务方法作拦截、过滤、预处理。
静态代理的优点是可以实现对目标方法的扩展,控制真实对象的访问权限,避免创建大对象,以及增强真实对象功能。业务类只需要关注业务逻辑本身,保证了业务类的重用性。把真实对象隐藏起来了,保护真实对象。
缺点是如果一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。
2 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3 业务类
// 真实类与代理类需实现相同的接口
public interface EmployeeService {
/*员工保存*/
public int save(String username,String password);
}
// 真实类
public class EmployeeServiceImpl implements EmployeeService {
@Override
public int save(String username, String password) {
System.out.println("员工姓名:"+username+"密码:"+password);
return 0;
}
}
4 代理类
public class EmployeeServiceProxy implements EmployeeService {
/*setter注入真实对象,增加事务操作*/
private EmployeeServiceImpl employeeService;
public void setEmployeeService(EmployeeServiceImpl employeeService) {
this.employeeService = employeeService;
}
/*setter注入事务管理对象*/
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/*执行真实对象的业务方法以及事务方法*/
@Override
public int save(String username, String password) {
try {
/*开启事务*/
transactionManager.begin();
/*调用真实的业务方法*/
employeeService.save(username,password);
/*设置异常*/
int num=10/0;
/*提交事务*/
transactionManager.commit();
} catch (Exception e) {
/*事务回滚*/
transactionManager.rollback();
e.printStackTrace();
}
return 0;
}
}
5 事务类
public class TransactionManager {
/*开启事务*/
public void begin(){
System.out.println("事务开启了.....");
}
/*提交事务*/
public void commit(){
System.out.println("事务提交了........");
}
/*回滚事务*/
public void rollback(){
System.out.println("事务回滚了.........");
}
}
6 resources配置文件 – spring-core.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--事务管理对象-->
<bean id="transactionManager" class="cn.tj.tx.TransactionManager"></bean>
<!--代理对象 为代理对象注入2个Bean-->
<bean id="employeeServiceProxy" class="cn.tj.proxy.EmployeeServiceProxy">
<property name="transactionManager" ref="transactionManager"></property>
<property name="employeeService" >
<bean class="cn.tj.service.impl.EmployeeServiceImpl"></bean>
</property>
</bean>
</beans>
7 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {
// employeeServiceProxy是bean的id 要一致
@Autowired
private EmployeeService employeeServiceProxy;
@Test
public void emp_save() {
//调用代理对象的方法
employeeServiceProxy.save("叶凡","19216875523");
}
}
三 JDK 动态代理
1 API
① java.lang.reflect.Proxy
Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
// 主要方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler hanlder)
// 参数
loader :类加载器,一般传递真实对象的类加载器;
interfaces:代理类需要实现的接口;
handler:代理执行处理器,说人话就是生成代理对象帮你要做什么。
返回:创建的代理对象。
// 作用
为指定类加载器、一组接口及调用处理器生成动态代理类实例。
② java.lang.reflect.InvocationHandler
由代理类实例的调用处理程序实现的接口
// 主要方法
public Object invoke(Object proxy, Method method, Object[] args)
// 参数
proxy :生成的代理对象;
method:当前调用的真实方法对象;
args :当前调用方法的实参。
返回:真实方法的返回结果。
// 作用
负责集中处理动态代理类上的所有方法调用,让使用者自定义做什么事情,对原来方法增强(加什么功能)。
2 代理类 – TransactionInvocationhandler
// jdk动态代理执行器 实现 InvocationHandler 接口,实现 invoke 方法,实现增强操作
public class TransactionInvocationhandler implements InvocationHandler {
/*注入真实对象:程序运行起来之后才会知道是什么对象*/
private Object object;
public void setObject(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
/*注入扩展对象:事务对象*/
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/*将真实业务方法和扩展方法组合执行*/
@Override
public Object invoke(Object proxy,
Method method,
Object[] args) throws Throwable {
Object o=null;
try {
/*开启事务*/
transactionManager.begin();
/*执行真实对象的方法:反射调用类成员方法*/
o= method.invoke(object,args);
/*设置异常*/
// int num=10/0;
/*提交事务*/
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
/*回滚事务*/
transactionManager.rollback();
}
return o;
}
}
3 配置文件 – spring-core.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--事务管理对象-->
<bean id="transactionManager" class="cn.tj.tx.TransactionManager"></bean>
<!--代理执行器对象-->
<bean id="transactionInvocationhandler" class="cn.tj.proxy.TransactionInvocationhandler">
<property name="transactionManager" ref="transactionManager"></property>
<property name="object" >
<bean class="cn.tj.service.impl.EmployeeServiceImpl"></bean>
</property>
</bean>
</beans>
4 真实类接口实现类与事务类不变
5 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {
@Autowired
private TransactionInvocationhandler transactionInvocationhandler;
@Test
public void emp_save() {
//创建代理对象 (静态方法) 返回值应为service 默认为Object 故强转
EmployeeService employePproxy = (EmployeeService) Proxy.newProxyInstance(
transactionInvocationhandler.getObject().getClass().getClassLoader(),//通过执行器对象获取到真实对象的类加载器
transactionInvocationhandler.getObject().getClass().getInterfaces(),//通过执行器对象获取真实对象实现的接口
transactionInvocationhandler);
System.out.println(employePproxy);
//调用代理对象的方法
// employePproxy.save("李四","333333");
}
}
四 CGLIB 动态代理
1 API
① org.springframework.cglib.proxy.Enhancer
Enhancer是CGLIB中用于字节码增强的类,类似于JDK动态代理中的Proxy类。它能够代理普通Java类和接口,通过创建一个被代理类的子类来拦截所有的方法调用。Enhancer的主要方法包括setSuperclass设置代理类的父类,setCallback设置回调函数等。
② org.springframework.cglib.proxy.InvocationHandler
类似 JDK 中 InvocationHandler,CGLIB 库中的一个接口,用于拦截目标对象的代理方法调用。当一个代理对象被调用时,会先调用该接口的实现类中的invoke方法,然后才会调用目标对象的方法
2 代理类 – TransactionInvocationhandler
// 导入cglib包的InvocationHandler
public class TransactionInvocationhandler implements InvocationHandler {
/*注入真实对象:程序运行起来之后才会知道是什么对象*/
private Object object;
public void setObject(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
/*注入扩展对象:事务对象*/
private TransactionManager transactionManager;
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/*将真实业务方法和扩展方法组合执行*/
@Override
public Object invoke(Object proxy,
Method method,
Object[] args) throws Throwable {
Object o=null;
try {
/*开启事务*/
transactionManager.begin();
/*执行真实对象的方法:反射调用类成员方法*/
o= method.invoke(object,args);
/*设置异常*/
// int num=10/0;
/*提交事务*/
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
/*回滚事务*/
transactionManager.rollback();
}
return o;
}
}
3 测试类
// 只须修改代理类和测试类 其他与jdk代理相同
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {
@Autowired
private TransactionInvocationhandler transactionInvocationhandler;
@Test
public void emp_save() {
//创建代理对象的生成类对象 通过他创建代理对象
Enhancer enhancer=new Enhancer();
//设置代理对象和真实对象的继承关系 将真实对象设为代理对象的父类
enhancer.setSuperclass(transactionInvocationhandler.getObject().getClass());
//设置代理对象需要执行的操作 形参为callback 实际传递为他的继承类的实现类
enhancer.setCallback(transactionInvocationhandler);
//创建代理对象
EmployeeServiceImpl employeeService = (EmployeeServiceImpl) enhancer.create();
//调用代理对象的方法
employeeService.save("陈汉生","admin");
}
}
五 动态代理小结
1 JDK 动态代理
Java 动态代理是使用 java.lang.reflect 包中的 Proxy 类与 InvocationHandler 接口这两个来完成的。要使用 JDK 动态代理,真实类必须实现接口。JDK 动态代理将会拦截所有 pubic 的方法(因为只能调用接口中定义的方法),这样即使在接口中增加了新的方法,不用修改代码也会被拦截。
动态代理的最小单位是类(类中某些方法都会被处理),如果只想拦截一部分方法,可以在 invoke 方法中对要执行的方法名进行判断。(代理类与真实类共同实现相同接口)
2 CGLIB 动态代理
CGLIB 可以生成真实类的子类,并重写父类非 final 修饰符的方法。要求类不能是 final 的,要拦截的方法要是非 final、非 static、非 private 的。
动态代理的最小单位是类(类中某些方法都会被处理),如果只想拦截一部分方法,可以在 invoke 方法中对要执行的方法名进行判断。(代理类是继承真实类)文章来源:https://www.toymoban.com/news/detail-823991.html
3 选择
从性能上看:Javassit 优于 CGLIB 优于 JDK,JDK 动态代理是基于实现接口的,CGLIB 和 Javassit 是基于继承真实类的。
真实类实现了接口,优先选用 JDK 动态代理。若真实类没实现任何接口,使用 Javassit 和 CGLIB 动态代理。文章来源地址https://www.toymoban.com/news/detail-823991.html
到了这里,关于Java研学-代理模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!