再回首:如何实现Spring AOP?

引言

在本课程中,我们学习了Spring AOP的实现和应用。Spring AOP是Spring框架的核心组件之一,它允许我们以面向切面的方式处理那些与业务逻辑无关,但却是多个业务代码共同需要的功能,如日志记录、安全检查和事务管理等。通过这种方式,我们可以提高代码的可复用性、可维护性和可扩展性。

Spring AOP核心概念

Aspect(切面)

Aspect指的是横切逻辑(cross-cutting concerns),即那些跨越多个业务逻辑的功能。Aspect通过Join point、Advice和Pointcut来定义,并在运行时自动应用到不同的Join point上。

Join point(连接点)

Join point是程序执行过程中的一个点,如方法的调用或异常的处理。

Advice(通知)

Advice是Aspect在Join point上的具体行为,如在方法执行前后添加日志。

Pointcut(切点)

Pointcut用于定义哪些Join point会被Advice影响。

AOP的实现

Spring AOP的实现基于JDK动态代理,虽然实现简单,但效率不高且有限制。我们通过以下步骤实现AOP:

  1. 引入Java动态代理技术,探讨如何动态插入业务逻辑。
  2. 引入Spring的Interceptor和Advice概念,进一步抽取动态业务逻辑。
  3. 引入Spring的PointCut概念,定义Advice作用范围。
  4. 通过自动化机制自动生成动态代理,免除手工配置PointCut和Interceptor的工作。

思考题与参考答案

思考题17:动态代理

如果MiniSpring想扩展到支持Cglib,程序应该从哪里下手改造?

参考答案

改造可以从以下几个方面进行:

  1. AopProxy接口:我们的动态代理包装在AopProxy这个接口中,对于JDK动态代理技术,使用了JdkDynamicAopProxy这个类来实现。对于Cglib技术,我们可以新增一个CglibAopProxy类进行实现。

  2. ProxyFactoryBean:采用哪一种AOP Proxy可以由工厂方法决定,也就是在ProxyFactoryBean中所使用的aopProxyFactory。它在初始化的时候有个默认实现,即DefaultAopProxyFactory。我们可以将这个类的createAopProxy()方法改造一下,以支持Cglib。

1
2
3
4
5
6
7
8
	public class DefaultAopProxyFactory implements AopProxyFactory {
		public AopProxy createAopProxy(Object target) {
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(target);
			}
			return new CglibAopProxy(config);
		}
	}

根据某些条件决定使用JdkDynamicAopProxy还是CglibAopProxy,或者通过配置文件给一个属性来配置也可以。

18|拦截器 :如何在方法前后进行拦截?

思考题

如果我们希望beforeAdvice能在某种情况下阻止目标方法的调用,应该从哪里下手改造改造我们的程序?

参考答案

答案在MethodBeforeAdviceInterceptor 的实现中,看它的invoke方法。

1
2
3
4
5
6
	public class MethodBeforeAdviceInterceptor implements MethodInterceptor {
		public Object invoke(MethodInvocation mi) throws Throwable {
			this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
			return mi.proceed();
		}
	}

这个方法先调用advice.before(),然后再调用目标方法。所以如果我们希望beforeAdvice能够阻止流程继续,可以将advice.before()接口改造成有一个boolean返回值,规定返回false则不调用mi.proceed()。

19|Pointcut :如何批量匹配代理方法?

思考题

我们现在实现的匹配规则是按照 * 模式串进行匹配,如果有不同的规则,应该如何改造呢?

参考答案

如果仍然按照名字来匹配,那就可以改造NameMatchMethodPointcut类,它现在的核心代码是:

1
2
3
4
5
6
	public class NameMatchMethodPointcut implements MethodMatcher,Pointcut{
		private String mappedName = "";
		protected boolean isMatch(String methodName, String mappedName) {
			return PatternMatchUtils.simpleMatch(mappedName, methodName);
		}
	}

功能扩展:增加正则表达式匹配方法

1. 现有实现

  • 默认实现使用的是PatternMatchUtils.simpleMatch(),适用于简单的模式匹配。

2. 新增功能

  • 增加一个名为regExprMatch()的方法,用于正则表达式匹配。

  • 该方法将接收一个正则表达式字符串,并根据该表达式进行匹配校验。

3. 扩展匹配规则

  • 如果需要超出名字匹配范围的匹配规则,可以增加一个OtherMatchMethodPointcut类和相应的Advisor类。

  • 用户需要自行实现这些类,并在配置文件中指定使用这个Advisor

4. 配置文件指定

  • 在配置文件中,可以指定使用自定义的Advisor,以便使用新的匹配规则。

5. 结构化和条理性

  • 上述内容已经按照结构化和条理性重新编写,以确保清晰易懂。
1
2
3
4
5
	<bean id="advisor" class="com.minis.aop.OtherMatchMethodPointcutAdvisor">
    </bean>
    <bean id="action" class="com.minis.aop.ProxyFactoryBean">
        <property type="String" name="interceptorName" value="advisor" />
    </bean>

20 | AutoProxyCreator:自动添加动态代理的实现

思考题

AOP在数据库事务处理中的应用 在面向切面编程(AOP)中,我们经常利用其特性来处理数据库事务。通过AOP,我们可以简化数据库事务的管理,使得代码更加简洁和易于维护。

参考答案

实现简单的事务处理 在数据库操作中,事务的处理通常涉及两个关键步骤:开启事务和提交事务。在不使用AOP的情况下,这通常需要在代码中显式地调用conn.setAutoCommit(false)来开启事务,以及在操作完成后调用conn.commit()来提交事务。通过AOP,我们可以将这些步骤封装成一个切面,自动在相应的方法执行前后进行调用。 使用MethodInterceptor实现 我们可以定义一个MethodInterceptor,在其中实现事务的自动管理。以下是实现这一功能的步骤:

  1. 开启事务:在目标方法执行前,通过conn.setAutoCommit(false)禁用自动提交,从而开启一个事务。

  2. 执行业务逻辑:执行目标方法中定义的数据库操作。

  3. 提交或回滚事务:根据业务逻辑的执行结果,决定是调用conn.commit()提交事务,还是conn.rollback()回滚事务。 通过这种方式,我们可以将事务管理的逻辑从业务代码中解耦出来,使得业务代码更加专注于业务逻辑本身,而事务管理则由AOP切面自动处理。

1
<bean id="transactionInterceptor" class="TransactionInterceptor" />

这个Interceptor拦截目标方法后添加事务处理逻辑,因此需要改造一下。

1
2
3
4
5
6
7
8
9
public class TransactionInterceptor implements MethodInterceptor{
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
conn.setAutoCommit(false);
Object ret=invocation.proceed();
conn.commit();
		return ret;
	}
}

从代码里可以看到,这里需要一个conn,因此我们要设法将数据源信息注入到这里。

我们可以抽取出一个TranactionManager类,大体如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class TransactionManager {
	@Autowired
	private DataSource dataSource;
	Connection conn = null;

	protected void doBegin() {
		conn = dataSource.getConnection();
		if (conn.getAutoCommit()) {
			conn.setAutoCommit(false);
		}
	}
	protected void doCommit() {
		conn.commit();
	}
}

由这个transaction manager负责数据源以及开始和提交事务,然后将这个transaction manager作为一个Bean注入Interceptor,因此配置应该是这样的。

1
2
3
4
5
<bean id="transactionInterceptor" class="TransactionInterceptor" >
    <property type="TransactionManager" name="transactionManager" value="txManager" />
</bean>
<bean id="txManager" class="TransactionManager">
</bean>

所以Interceptor最后应该改造成这个样子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class TransactionInterceptor implements MethodInterceptor{
  TransactionManager transactionManager;
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
transactionManager.doBegin();
Object ret=invocation.proceed();
transactionManager.doCommit();
		return ret;
	}
}