Spring 学习之AOP总结之实现代理(七)

Spring 源码系列

1、Spring 学习之扩展点总结之后置处理器(一)
2、Spring 学习之扩展点总结之后置处理器(二)
3、Spring 学习之扩展点总结之自定义事件(三)
4、Spring 学习之扩展点总结之内置事件(四)
5、Spring 学习之扩展点总结之@Import(五)
6、Spring 学习之AOP总结之基础介绍(六)
7、Spring 学习之AOP总结之实现代理(七)
8、SpringBoot 学习之自动配置基本原理(八)
9、SpringBoot 学习之启动原理(九)
10、ElasticSearch学习随笔之SpringBoot Starter 操作
11、图数据库 Neo4j 学习之SpringBoot整合
12、SpringBoot 学习之常用 Filter / Advice 总结
13、SpringBoot+FastJson 优雅的过滤 Response Body
14、Spring Security + Oauth2 认证授权

前言

在 Spring之 AOP 基础篇中介绍了 AOP 的原理,此篇上代码,主要以 JDK 动态代理实现。

一、ProxyFactoryBean 实现代理

打印日志是项目中最基本的也是最常见的操作,比如下单操作,就需要记录日志以便追踪订单,就用一个简单的下单操作并记录日志来模拟。

1.1、创建被代理对象

首先创建一个订单服务接口 OrderService 接口,并且创建具体的实现类 OrderServiceImpl 实现订单接口。

订单接口:

public interface OrderService {

	int save(String productName);
}

订单实现类:

@Component
public class OrderServiceImpl implements OrderService{

	@Override
	public int save(String productName) {
		System.out.println("生成订单操作!");
		return 1;
	}
}

1.2、创建代理对象

这里主要使用 advice 和 interceptor 来实现,创建一个下单操作前打印日志类 LogBeforeAdvice 并且实现 aop 提供的 advice 接口 MethodBeforeAdvice,再创建一个下单操作前后拦截操作的类 LogInterceptor 并且实现 aop 提供的 MethodInterceptor 接口。

advice 代理类:

public class LogBeforeAdvice implements MethodBeforeAdvice {

	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		String methodName = method.getName();
		System.out.println("生成订单方法【 " + methodName + " 】的前置操作,入参是【"+ Arrays.asList(args) +"】");
	}
}

interceptor 代理类:

public class LogInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println(getClass()+"调用方法前");
		Object obj = invocation.proceed();
		System.out.println(getClass()+"调用方法后");
		return obj;
	}
}

1.3、获取代理对象

这里就用 Configuration 配置类里面,@Bean 的方式来创建所有的需要的对象并有 Spring IOC 管理。
用 ProxyFactoryBean 来生产代理对象,ProxyFactoryBean 实现了 FactoryBean,就可以用 getObject 方法来生产代理对象了。

@Configuration
public class MainConfig {

	/**
	 * 创建订单对象
	 */
	@Bean
	public OrderService orderService(){
		return new OrderServiceImpl();
	}

	/**
	 * 创建 Advice 通知对象
	 */
	@Bean
	public LogBeforeAdvice logBeforeAdvice(){
		return new LogBeforeAdvice();
	}

	/**
	 * 创建 Interceptor 拦截对象
	 */
	@Bean
	public LogInterceptor logInterceptor(){
		return new LogInterceptor();
	}

	/**
	 * 使用 FactoryBean 的 getObject方法来生产代理对象
	 */
	@Bean
	public ProxyFactoryBean orderServiceProxy(){
		ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
		proxyFactoryBean.setInterceptorNames("logBeforeAdvice", "logInterceptor");
		proxyFactoryBean.setTarget(orderService());
		return proxyFactoryBean;
	}
}

1.4、调用方法

在 main 方法中用 AnnotationConfigApplicationContext 来生产上下文,并且获取到代理对象,进行调用测试。

public class MainStart {

	public static void main(String[] args) {

		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
		OrderService orderService = applicationContext.getBean("orderServiceProxy", OrderService.class);
		orderService.save("核桃花生牛奶(箱)");
	}
}

调用结果:
测试结果

从调用结果中可以看到,使用了责任链的方式对 Advice 和 Interceptor 进行了调用,这个例子非常简单,就是提供了 被代理对象 和 代理对象,利用 FactoryBean 的 getObject 对象来创建了一个代理对象。
代理模式需要一个接口(也可以没有),需要一个具体的实现类,定义一个代理类用来包装实现类,在使用的时候需要代理类来生成实例。

二、NameMatchMethodPointcutAdvisor 实现代理

2.1、创建代理对象

使用 NameMatchMethodPointcutAdvisor 的方法实现,就是加入了 Advicor 的概念,什么是 Advisor呢?我们可以称它为一个通知者,有它决定谁可以被代理实现,Advisor 内部有一个 Advice,Advice 负责实现方法的包装,而 Advisor 则负责匹配方法。
可以发现,advisor。setMappedNames 方法可以设置多个方法名,可以是不同对象的方法,这样就比上面的方法更加方便灵活的实现多个代理,Advicor更细粒度的控制了方法代理。

在 Configuration 配置类中加入生成 NameMatchMethodPointcutAdvisor 配置。

	/**
	 * 利用方法匹配的方法生产 Advisor
	 */
	@Bean
	public NameMatchMethodPointcutAdvisor methodPointcutAdvisor(){
		NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
		advisor.setAdvice(logBeforeAdvice());
		advisor.setMappedNames("save", "pay");
		return advisor;
	}

在 Configuration 配置类中用 ProxyFactoryBean 指定 Advicor 生成代理对象。

@Bean
	public ProxyFactoryBean orderServiceProxy(){
		ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
		proxyFactoryBean.setInterceptorNames("methodPointcutAdvisor");
		proxyFactoryBean.setTarget(orderService());
		return proxyFactoryBean;
	}

2.2、调用方法

public class MainStart {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
		OrderService orderService = applicationContext.getBean("orderServiceProxy", OrderService.class);
		orderService.save("核桃花生牛奶(箱)");
		orderService.pay(100);
	}
}

测试结果:
测试结果

从测试结果中可以看出,save 方法和 pay 方法都实现代理了。
到这里,Advice、Advisor、Interceptor 三个概念都介绍完了,不过可以发现,这种实现方法并不是特别方便,我们还需要通过 getBean 方法获取到代理对象并且调用,这是特别不方便的,下面来看看 AutoProxy 代理。

三、AutoProxy实现代理

上面的方法都是明显的指定代理类的 Bean,这样就不能灵活的动态生成配置,AutoProxy 强调自动,那么如何自动呢,Spring 会自动的做生成代理这件事情,使用 BeanNameAutoProxyCreator 来实现自动代理。

3.1、BeanNameAutoProxyCreator 创建代理对象

在 Configuration 配置类中加入生成 BeanNameAutoProxyCreator 配置。

	@Bean
	public BeanNameAutoProxyCreator autoProxyCreator(){
		BeanNameAutoProxyCreator autoProxyCreator = new BeanNameAutoProxyCreator();
		//设置需要创建代理Bean名称
		autoProxyCreator.setBeanNames("order*");
		//设置拦截代理对象名称
		autoProxyCreator.setInterceptorNames("logInterceptor");
		return autoProxyCreator;
	}

3.2、代理测试

public class MainStart {

	public static void main(String[] args) {

		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
		OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
		orderService.save("核桃花生牛奶(箱)");
		orderService.pay(100);
	}
}

测试结果:
测试结果

测试结果中看,我们不需要关系代理哪些对象,Spring 通过 order* 已经找到了所有可以代理的对象并生成了代理。

3.3、DefaultAdvisorAutoProxyCreator 创建代理对象

上面是用的 BeanNameAutoProxyCreator 制定方法名称(模糊匹配)自己匹配方法来交给 Advice 处理。上面说到,Advisor 包装了 Advice ,Advisor 负责拦截,Advice 负责处理,所以可不可以实现 Advicor 全局拦截找到需要代理的Bean。
不过,在相同的包下面,还有个弟弟 DefaultAdvisorAutoProxyCreator 使用更加方便,它是在 IOC 容器中所有的 Advisor 来匹配方法,Advice 来处理。

1、 在 Configuration 配置类中加入生成 RegexpMethodPointcutAdvisor 配置,利用正则来实现包的扫描找到所有的 Advisor。

    @Bean
	public RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor(){
		RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor();
		advisor.setAdvice(logInterceptor());
		advisor.setPattern("com.self.*");
		return advisor;
	}

2、配置 DefaultAdvisorAutoProxyCreator 使所有的 Advisor 生效,无须其他配置。

	@Bean
	public DefaultAdvisorAutoProxyCreator autoProxyCreator(){
		return new DefaultAdvisorAutoProxyCreator();
	}

3.4、代理测试

public class MainStart {

	public static void main(String[] args) {

		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
		OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
		orderService.save("核桃花生牛奶(箱)");
		orderService.pay(100);
	}
}

测试结果:
测试结果

从测试结果中可以看出,所有的 Advicor 都生效了,并且被拦截生成代理了。

四、总结

AOP 呢,就是动态代理,Spring 中提供了很多生成代理的方法,此篇中从一个简单的代理到方法名匹配代理,再到正则指定包路径生成代理,越来越方便。也就大概知道 Spring 中的 AOP 大概都有哪些实现方式,不过真正的实现思路还是需要去深究 Spring 里面怎么实现的。
敬请期待后面源码研究。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值