代理模式
给一个对象提供一种代理对象以控制对该对象的访问。代理对象在不改变原对象代码的基础上对原对象的功能进行扩展。
使用代理降低了系统的耦合度,扩展性也会更好,还可以起到保护目标对象的作用。代理模式在 Java 开发中是广泛应用的,特别是在框架中底层原理经常涉及到代理模式(尤其是动态代理)。
静态代理和动态代理,实际使用时还是动态代理使用的比较多。原因就是静态代理需要自行手写代码,维护、修改非常繁琐,会额外引入很多工作量。也不能很好的使用配置完成逻辑的指定,所以使用较少。
- 静态代理维护复杂,一旦接口或父类发生改变,所有相关的类或接口就都得进行维护。
- 动态代理无需手写代理类,也不会存在代码编译的过程。运用在内存中生产代理类的技术在 JVM 的运行区造一个代理对象,只需对需要修改的部分进行编辑。
基于 JDK 和基于 CGLIB,实际使用时两个都会用到。
在 Spring 中,默认情况下它就支持了两种动态代理方式。如果你指定的目标类实现了接口,Spring 就会自动选择 JDK 的动态代理。而如果目标类没有实现接口,则 Spring 会使用 CGLIB。而在 SpringBoot 中,默认使用的是 CGLIB。
我们在开发时,由于基于 JDK 的动态代理要求比较多,更不容易实现,所以很多人习惯于统一配置为使用 CGLIB 进行代理。也就是 CGLIB 更通用。
【静态代理】基于接口实现
让目标对象和代理对象都实现一个共同接口。那么这两个类就有了公共的方法,就可以在代理对象中实现对目标对象功能的扩展。
public class StaticProxyInterfaceDemo {
/**
* 公共接口
*/
public interface Dao {
void save();
}
/**
* 原始对象
*/
static class UserDao implements Dao {
@Override
public void save() {
System.out.println("保存数据");
}
}
/**
* 代理对象
*/
static class UserDaoProxy implements Dao {
private UserDao userDao;
@Override
public void save() {
System.out.println("开启事务");
userDao.save();
System.out.println("提交事务");
}
}
}
【静态代理】基于继承实现
让代理对象继承目标对象,那么代理对象就拥有目标对象的方法,同时又可以对其功能进行扩展。
public class StaticProxyExtendsDemo {
/**
* 原始对象
*/
static class UserDao {
public void save() {
System.out.println("保存数据");
}
}
/**
* 代理对象
*/
static class UserDaoProxy extends UserDao {
@Override
public void save() {
System.out.println("开启事务");
super.save();
System.out.println("提交事务");
}
}
}
【动态代理】基于接口的 JDK 动态代理
JDK 动态代理基于接口实现,要求原始对象必须实现接口。
public class JdkProxyDemo {
public static void main(String[] args) {
// 创建目标对象(如果是 JDK 8 之前的版本,匿名内部类访问外面的变量需要声明 final)
UserService userService = new UserServiceImpl();
Class<? extends UserService> userServiceClass = userService.getClass();
// 使用 JDK 动态代理生成代理对象
UserService proxyObj = (UserService) Proxy.newProxyInstance(userServiceClass.getClassLoader(), userServiceClass.getInterfaces(), (proxy, method, args1) -> {
// 在此处编写前置操作
System.out.println("Before");
Object result = null;
try {
result = method.invoke(userService, args1);
} catch (Exception e) {
// 在此处编写异常操作
System.out.println("Exception");
}
// 在此处编写后置操作
System.out.println("After");
// 可以在此处编写新的返回值并返回
return result;
});
proxyObj.login();
}
}
【动态代理】基于继承的 CGLIB 动态代理
CGLIB 动态代理基于继承原始类实现,不需要原始类实现接口,但是原始类不能是 final。
public class CglibProxyDemo {
public static void main(String[] args) {
// 创建目标对象
UserService userService = new UserServiceImpl();
// 使用 CGlib 动态创建代理对象
UserService proxyObj = (UserService) Enhancer.create(userService.getClass(), (InvocationHandler) (o, method, objects) -> {
// 在此处编写前置操作
System.out.println("Before");
Object result = null;
try {
result = method.invoke(userService, args);
} catch (Exception e) {
// 在此处编写异常操作
System.out.println("Exception");
}
// 在此处编写后置操作
System.out.println("After");
// 可以在此处编写新的返回值并返回
return result;
});
proxyObj.login();
}
}
Spring AOP
Spring 的 AOP(面向切面编程)是 Spring 框架中的一个核心特性,它允许开发者在不修改原有代码的情况下,通过添加额外的逻辑来实现横切关注点(cross-cutting concerns)的功能。
Spring 的 AOP 基于代理模式实现。它通过动态代理或者字节码生成技术,在运行时为目标对象生成一个代理对象,并将切面逻辑织入到代理对象的方法调用中。当应用程序调用代理对象的方法时,切面逻辑会在目标方法执行前、执行后或抛出异常时被触发执行。
在 Spring 中,默认情况下它就支持了两种动态代理方式。如果你指定的目标类实现了接口,Spring 就会自动选择 JDK 的动态代理。而如果目标类没有实现接口,则 Spring 会使用 CGLIB。而在 SpringBoot 中,默认使用的是 CGLIB。
关于 Spring AOP 的详细介绍,可以查看这篇文章:《Spring AOP:解锁切面编程的威力与实践》。
我们已经了解到,Spring 的 AOP 本质上就是动态代理,那么 Spring 是通过什么来应用这个动态代理技术的呢?回想之前学习的内容,我们知道 BeanPostProcesser 可以对创建的 Bean 进行加工,而 Spring 的 AOP 正好就是用 BeanPostProcesser 来实现的,它就是 AnnotationAwareAspectJAutoProxyCreator。
Spring 基于注解的 AOP 编程,我们需要在配置类中标记
@EnableAspectJAutoProxy
注解,在 SpringBoot 中我们不需要手动指定,因为 SpringBoot 底层已经进行了封装。如果我们要强制 Spring 每次都使用 CGLIB,那么需要在这个注解添加属性proxyTargetClass = true
。为什么我们指定了 @EnableAspectJAutoProxy 就可以了?我们知道 AOP 是通过 AnnotationAwareAspectJAutoProxyCreator 这个 BeanPostProcesser 来实现的,那么 @EnableAspectJAutoProxy 底层肯定也是通过 @Import 技术引入了这个类。这样就不需要我们自己引入,屏蔽了 Spring 的 AOP 实现细节,语义化的处理也更加的通俗。
@EnableAspectJAutoProxy 还有一个属性 exposeProxy,默认为 false,此参数控制是否将代理对象放入 ThreadLocal,以方便后续获取,解决代理对象的方法中调用本来其他方法代理失效的问题。什么时候设置的?可以参考 JdkDynamicAopProxy.invoke 方法
我们需要在代理类中配置:
@EnableAspectJAutoProxy(exposeProxy=true)
,随后在代理的原始方法调用其他方法的时候,通过AopContext.currentProxy()
获取本类的代理对象,然后调用方法。@Configuration @EnableAspectJAutoProxy(exposeProxy = true) @ComponentScan("world.xuewei.aopproxy") public class AopProxyConfig { }
@Service public class UserService implements IService { @Override public void m1() { System.out.println("UserService.m1"); // 注意这里,实现接口默认是 JDK 动态代理,这里获取的代理对象的类型用接口类型接收 IService service = (IService) AopContext.currentProxy(); // 如果是 CGLIB 动态代理,则可以使用原始对象类型接收,同时需要指定 proxyTargetClass = true // UserService service = (UserService) AopContext.currentProxy(); service.m2(); } @Override public void m2() { System.out.println("UserService.m2"); } }
手写 Spring AOP
基于前面的了解,我们手写一个简单的实现原理,其实就是创建一个 BeanPostProcesser。这个 BeanPostProcesser 通常是在初始化完成后去执行的。当然也不一定,也有可能在处理循环引用的时候提前就执行了。
定义接口和原始类
public interface IStudentService {
void test1();
}
@Service
public class StudentServiceImpl implements IStudentService {
@Override
public void test1() {
System.out.println("StudentServiceImpl.test1");
}
}
定义代理 BeanPostProcessor
public class MyProxyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 基于 CGLIB 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(this.getClass().getClassLoader());
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
// 在此处编写前置操作
System.out.println("Before");
Object result = null;
try {
result = method.invoke(bean, objects);
} catch (Exception e) {
// 在此处编写异常操作
System.out.println("Exception");
}
// 在此处编写后置操作
System.out.println("After");
// 可以在此处编写新的返回值并返回
return result;
});
return enhancer.create();
}
}
定义 AOP 开启注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyProxyBeanPostProcessor.class)
public @interface EnableMyProxy {
}
定义配置类
@EnableMyProxy
@Configuration
@ComponentScan("world.xuewei.proxy")
public class ProxyAppConfig {
}
定义测试类
public class ProxyTest {
private ApplicationContext context;
@Before
public void before() {
// 指定配置类,创建 Spring 工厂
context = new AnnotationConfigApplicationContext(ProxyAppConfig.class);
}
/**
* 测试 AOP
*/
@Test
public void test1() {
StudentServiceImpl service = context.getBean("studentServiceImpl", StudentServiceImpl.class);
service.test1();
}
}
[外链图片转存中…(img-Ly5hHRy3-1722864552441)]
通过上面的编码,我们已经实现了最简单的 AOP,但是会存在一些问题:
- 我们通过 MyProxyBeanPostProcessor 为所有类的所有方法都进行了代理,这里显然不够灵活。
- 我们通过 MyProxyBeanPostProcessor 在增加额外功能的时候,增强位置和增强逻辑都是写死在代理中的,这里显然不够灵活。
此处我们可以利用 Spring 提供的 PointCut 切入点接口,对我们上面的 AOP 实现进行升级。
升级版 Spring AOP
定义自定义切入点配置
主要解决之前为所有类的所有方法都进行了代理,不够灵活的问题。
@Component
public class MyPointCut implements Pointcut {
/**
* 类型过滤器,判定当前的类型是否满足 AOP 要求
*/
@Override
public ClassFilter getClassFilter() {
return clazz -> {
// 只为 StudentServiceImpl 类做代理
return StudentServiceImpl.class.isAssignableFrom(clazz);
};
}
/**
* 方法过滤器,判定当前的方法是否满足 AOP 要求
*/
@Override
public MethodMatcher getMethodMatcher() {
return new MethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return matches(method, targetClass, null);
}
@Override
public boolean isRuntime() {
return true;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
// 只为 test2 方法进行增强
return "test2".equals(method.getName());
}
};
}
}
改造代理 BeanPostProcessor
public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 获取自定义的切入点
Pointcut pointcut = beanFactory.getBean(Pointcut.class);
ClassFilter classFilter = pointcut.getClassFilter();
MethodMatcher methodMatcher = pointcut.getMethodMatcher();
// 基于 CGLIB 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(this.getClass().getClassLoader());
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
Object result = null;
if (classFilter.matches(o.getClass()) && methodMatcher.matches(method, o.getClass())) {
// 满足条件,添加增强逻辑
// 在此处编写前置操作
System.out.println("Before");
try {
result = method.invoke(bean, objects);
} catch (Exception e) {
// 在此处编写异常操作
System.out.println("Exception");
}
// 在此处编写后置操作
System.out.println("After");
// 可以在此处编写新的返回值并返回
} else {
// 不满足条件直接执行原始方法
result = method.invoke(bean, objects);
}
return result;
});
return enhancer.create();
}
}
使用一系列回调接口
MethodBeforeAdvice、AfterReturningAdvice
主要解决在增加额外功能的时候,增强位置和增强逻辑都是写死在代理中的,不够灵活的问题。
@Component
public class MyAdviceBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("Before");
}
}
@Component
public class MyAdviceAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
System.out.println("After");
}
}
public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 获取自定义的切入点
Pointcut pointcut = beanFactory.getBean(Pointcut.class);
ClassFilter classFilter = pointcut.getClassFilter();
MethodMatcher methodMatcher = pointcut.getMethodMatcher();
// 获取一系列回调接口
MethodBeforeAdvice beforeAdvice = beanFactory.getBean(MethodBeforeAdvice.class);
AfterReturningAdvice afterReturningAdvice = beanFactory.getBean(AfterReturningAdvice.class);
// 基于 CGLIB 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(this.getClass().getClassLoader());
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
Object result = null;
if (classFilter.matches(o.getClass()) && methodMatcher.matches(method, o.getClass())) {
// 满足条件,添加增强逻辑
// 回调前置增强方法
beforeAdvice.before(method, objects, o.getClass());
try {
result = method.invoke(bean, objects);
} catch (Exception e) {
// 在此处编写异常操作
System.out.println("Exception");
}
// 回调后置增强方法
afterReturningAdvice.afterReturning(result, method, objects, o.getClass());
} else {
// 不满足条件直接执行原始方法
result = method.invoke(bean, objects);
}
return result;
});
return enhancer.create();
}
}
包装自定义切面
在 Spring 中,切入点和额外功能组成了切面(通知),相当于对这两者再次进行了封装,所以上面我们在 BeanPostProcessor 中直接使用切入点和额外功能还是有点生硬,不太合理。
@Component
public class MyPointCutAdvisor implements PointcutAdvisor, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public Pointcut getPointcut() {
return new MyPointCut();
}
@Override
public Advice getAdvice() {
return beanFactory.getBean(Advice.class);
}
@Override
public boolean isPerInstance() {
return false;
}
}
public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 获取自定义的切入点
PointcutAdvisor pointcutAdvisor = beanFactory.getBean(PointcutAdvisor.class);
Pointcut pointcut = pointcutAdvisor.getPointcut();
Advice advice = pointcutAdvisor.getAdvice();
// 基于 CGLIB 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(this.getClass().getClassLoader());
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
Object result = null;
if (pointcut.getClassFilter().matches(o.getClass()) && pointcut.getMethodMatcher().matches(method, o.getClass())) {
// 满足条件,添加增强逻辑
if (advice instanceof BeforeAdvice) {
// 如果当前通知是前置通知,则回调前置增强方法
MethodBeforeAdvice beforeAdvice = (MethodBeforeAdvice) advice;
beforeAdvice.before(method, objects, o.getClass());
}
result = method.invoke(bean, objects);
if (advice instanceof AfterAdvice) {
// 如果当前通知是后置通知,则回调后置增强方法
AfterReturningAdvice afterReturningAdvice = (AfterReturningAdvice) advice;
afterReturningAdvice.afterReturning(result, method, objects, o.getClass());
}
} else {
// 不满足条件直接执行原始方法
result = method.invoke(bean, objects);
}
return result;
});
return enhancer.create();
}
}
这里我们定义的只是单一切面,单个切入点和单个额外功能,如果要完善的话就可以参考 Spring AOP,将切面组成链路,其实就是遍历执行。
public class MyProxyBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 如果当前是一个代理对象,则直接返回
if (bean instanceof Advisor) {
return bean;
}
// 获取所有的通知增强器
String[] pointcutAdvisorNames = beanFactory.getBeanNamesForType(PointcutAdvisor.class);
List<PointcutAdvisor> pointcutAdvisors = Arrays.stream(pointcutAdvisorNames)
.map(name -> beanFactory.getBean(name, PointcutAdvisor.class))
.collect(Collectors.toList());
List<PointcutAdvisor> beforeAdvisors = pointcutAdvisors.stream()
.filter(pointcutAdvisor -> pointcutAdvisor.getAdvice() instanceof BeforeAdvice)
.collect(Collectors.toList());
List<PointcutAdvisor> afterAdvisors = pointcutAdvisors.stream()
.filter(pointcutAdvisor -> pointcutAdvisor.getAdvice() instanceof AfterAdvice)
.collect(Collectors.toList());
// 基于 CGLIB 创建代理对象
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(this.getClass().getClassLoader());
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((InvocationHandler) (o, method, objects) -> {
Object result = null;
for (PointcutAdvisor beforeAdvisor : beforeAdvisors) {
Pointcut pointcut = beforeAdvisor.getPointcut();
if (pointcut.getClassFilter().matches(o.getClass()) && pointcut.getMethodMatcher().matches(method, o.getClass())) {
// 满足条件,添加增强逻辑
Advice advice = beforeAdvisor.getAdvice();
// 如果当前通知是前置通知,则回调前置增强方法
MethodBeforeAdvice beforeAdvice = (MethodBeforeAdvice) advice;
beforeAdvice.before(method, objects, o.getClass());
}
}
result = method.invoke(bean, objects);
for (PointcutAdvisor afterAdvisor : afterAdvisors) {
Pointcut pointcut = afterAdvisor.getPointcut();
if (pointcut.getClassFilter().matches(o.getClass()) && pointcut.getMethodMatcher().matches(method, o.getClass())) {
// 满足条件,添加增强逻辑
Advice advice = afterAdvisor.getAdvice();
// 如果当前通知是前置通知,则回调前置增强方法
AfterReturningAdvice afterReturningAdvice = (AfterReturningAdvice) advice;
afterReturningAdvice.afterReturning(result, method, objects, o.getClass());
}
}
return result;
});
return enhancer.create();
}
}