代理设计模式
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。
Spring中的AOP代理,可以是JDK动态代理,也可以是CGLIB代理
JDK动态代理
JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象
1、创建Service层的接口和实现类,实现两个方法
public interface UserService {
int insertUser();
int deleteUser();
}
public class UserServiceImpl implements UserService {
public int insertUser() {
System.out.println("insertUser...");
return 0;
}
public int deleteUser() {
System.out.println("deleteUser...");
return 0;
}
}
2、创建切面类
- 也就是要在某个方法的前后执行的方法
- (这里也采用接口的方式创建)
public interface MyAspect {
void beginTransaction();
void closeTransaction();
}
public class MyAspectImpl implements MyAspect {
public void beginTransaction() {
System.out.println("模拟开启事务...");
}
public void closeTransaction() {
System.out.println("模拟关闭事务");
}
}
3、创建JDK代理
- 需要创建一个代理对象,执行Service中方法的时候,由代理对象来执行
- method.invoke就是执行调用的方法
package com.robot.proxy;
import com.robot.aspect.MyAspect;
import com.robot.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理。
*
* @Author 张宝旭
* @Date 2020/9/28
*/
public class JdkProxy implements InvocationHandler {
UserService userService; // 方法参数传递
MyAspect myAspect; // setter方法注入
public void setMyAspect(MyAspect myAspect) {
this.myAspect = myAspect;
}
// 创建代理对象
public Object createProxy(UserService userService) {
this.userService = userService;
// 当前类的类加载器
ClassLoader classLoader = JdkProxy.class.getClassLoader();
// 被代理对象实现的所有接口
Class<?>[] clazz = userService.getClass().getInterfaces();
// 返回代理对象
return Proxy.newProxyInstance(classLoader, clazz, this);
}
// 执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行前置增强方法
myAspect.beginTransaction();
// 执行调用的方法
Object result = method.invoke(userService, args);
// 执行后置增强方法
myAspect.closeTransaction();
return result;
}
}
4、配置applicationContext.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 name="myAspect" class="com.robot.aspect.impl.MyAspectImpl"/>
<bean name="userService" class="com.robot.service.impl.UserServiceImpl"/>
<bean name="jdkProxy" class="com.robot.proxy.JdkProxy">
<property name="myAspect" ref="myAspect"/>
</bean>
</beans>
5、测试
public class JdkProxyTest {
@Test
public void JdkProxyTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 创建代理
JdkProxy jdkProxy = (JdkProxy) applicationContext.getBean("jdkProxy");
// 创建被代理对象
UserService userService = (UserService) applicationContext.getBean("userService");
// 创建代理对象
UserService proxy = (UserService) jdkProxy.createProxy(userService);
// 使用代理执行方法
proxy.insertUser();
proxy.deleteUser();
}
}
结果
模拟开启事务...
insertUser...
模拟关闭事务
模拟开启事务...
deleteUser...
模拟关闭事务
结果表明,在调用insertUser方法和deleteUser方法的时候,都在其前后执行了指定的方法,也就是将切面类中的方法,切入到指定方法的前后执行
- 如果由对象本身去执行此方法,则只是输出调用方法的输出结果
- 如果由代理对象去执行此方法,那么会输出如上显示的结果(前后都被添加了增强方法)
cglib动态代理
1、创建一个实现类
package com.robot.service.impl;
/**
* cglib动态代理测试实现类。
*
* @Author 张宝旭
* @Date 2020/9/29
*/
public class CartService {
public void insert() {
System.out.println("cglib insert...");
}
public void delete() {
System.out.println("cglib delete...");
}
}
2、创建切面类(和上面JDK动态代理的通用)
package com.robot.aspect;
/**
* @Author 张宝旭
* @Date 2020/9/29
*/
public interface MyAspect {
void beginTransaction();
void closeTransaction();
}
package com.robot.aspect.impl;
import com.robot.aspect.MyAspect;
/**
* 切面类。
*
* @Author 张宝旭
* @Date 2020/9/28
*/
public class MyAspectImpl implements MyAspect {
public void beginTransaction() {
System.out.println("模拟开启事务...");
}
public void closeTransaction() {
System.out.println("模拟关闭事务");
}
}
3、创建代理工厂
package com.robot.proxy;
import com.robot.aspect.MyAspect;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglib动态代理。
*
* @Author 张宝旭
* @Date 2020/9/29
*/
public class CglibProxy implements MethodInterceptor {
// 创建切面类,setter方法注入
MyAspect myAspect;
public void setMyAspect(MyAspect myAspect) {
this.myAspect = myAspect;
}
// 创建代理对象
public Object createProxy(Object target) {
// 创建动态类对象
Enhancer enhancer = new Enhancer();
// 确定需要增强的类
enhancer.setSuperclass(target.getClass());
// 添加回调函数
enhancer.setCallback(this);
// 返回创建的代理类
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 执行前置增强
myAspect.beginTransaction();
// 执行调用的方法
Object result = methodProxy.invokeSuper(o, objects);
// 执行后置增强
myAspect.closeTransaction();
return result;
}
}
4、配置applicationContext.xml文件
- (接着上面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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="myAspect" class="com.robot.aspect.impl.MyAspectImpl"/>
<bean name="cartService" class="com.robot.service.impl.CartService"/>
<bean name="userService" class="com.robot.service.impl.UserServiceImpl"/>
<bean name="jdkProxy" class="com.robot.proxy.JdkProxy">
<property name="myAspect" ref="myAspect"/>
</bean>
<bean name="cglibProxy" class="com.robot.proxy.CglibProxy">
<property name="myAspect" ref="myAspect"/>
</bean>
</beans>
5、测试
package com.robot.proxy;
import com.robot.service.impl.CartService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 测试cglib动态代理。
*
* @Author 张宝旭
* @Date 2020/9/29
*/
public class CglibProxyTest {
@Test
public void cglibProxyTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
CglibProxy cglibProxy = (CglibProxy) applicationContext.getBean("cglibProxy");
CartService cartService = (CartService) applicationContext.getBean("cartService");
CartService proxy = (CartService) cglibProxy.createProxy(cartService);
proxy.insert();
proxy.delete();
}
}
结果
模拟开启事务...
cglib insert...
模拟关闭事务
模拟开启事务...
cglib delete...
模拟关闭事务
和上面JDK动态代理的结果相同,说明也成功的使用了代理来执行指定方法,并在前后都执行了指定增强方法
总结
JDK动态代理需要使用接口的方式,是基于接口的,假如将以上JDK动态代理的例子中UserService这个接口去掉,直接写它的实现类,那么则会报错,因为必须要写接口
cglib则不需要使用接口,是基于继承实现的,直接创建实现类即可,比如以上cglib动态代理的例子,无需写接口
- Spring的代理底层使用的就是这两种代理,可以根据需要,指定具体使用哪种代理生成机制(默认为JDK动态代理)
- 基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理