spring---aop(6)---Spring AOP中ProxyFactoryBean介绍
写在前面
这篇文章里面就要说说Spring自己的AOP,搞清楚哪种方式是Spring自己实现的AOP,哪种方式是Spring引入aspectj的AOP。
简单例子
Spring自己的AOP实现在于ProxyFactoryBean。先看下使用案例(仍和之前的案例是一样的):接口AService、实现类AServiceImpl、通知MyBeforeAdvice 。
public interface AService {
public void barA();
public void barB();
}
public class AServiceImpl implements AService{
@Override
public void barA() {
System.out.println("AServiceImpl.barA()");
this.barB();
}
@Override
public void barB() {
System.out.println("AServiceImpl.barB()");
}
}
public class MyBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("run my before advice");
}
}
然后就是xml的配置:
<bean id="aServiceImpl" class="com.xxx.plus.aop.demo.AServiceImpl"/>
<bean id="myBeforAdvice" class="com.xxx.plus.aop.demo.MyBeforeAdvice"/>
<bean id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces" value="com.xxx.plus.aop.demo.AService"/>
<property name="target">
<ref bean="aServiceImpl"/>
</property>
<property name="interceptorNames">
<list>
<value>myBeforAdvice</value>
</list>
</property>
</bean>
然后就可以使用了:
@Resource
private AService aService; @Test
public void testAOP(){
aService.barA();
}
运行这个单元测试,然后你就会看到报如下错误:
No qualifying bean of type [com.xxx.aop.service.AService] is defined: expected single matching bean but found : aServiceImpl,org.springframework.aop.framework.ProxyFactoryBean#
原因就是对于接口AService,有两个实现类aServiceImpl和ProxyFactoryBean所生产的代理类。所以我们不能使用@Resource(它是按类型注入),所以要使用按名称注入,我们怎么获取ProxyFactoryBean所产生的代理类的名称呢?其实就是ProxyFactoryBean配置的名称。因为ProxyFactoryBean实现了FactoryBean接口,对于这种接口从容器中获取该bean,不是获取的本身而是获取他的getObject方法所返回的值,看FactoryBean的文档:
/**
* Interface to be implemented by objects used within a {@link BeanFactory}
* which are themselves factories. If a bean implements this interface,
* it is used as a factory for an object to expose, not directly as a bean
* instance that will be exposed itself.
*
* <p><b>NB: A bean that implements this interface cannot be used as a
* normal bean.</b> A FactoryBean is defined in a bean style, but the
* object exposed for bean references ({@link #getObject()} is always
* the object that it creates.
*
* <p>FactoryBeans can support singletons and prototypes, and can
* either create objects lazily on demand or eagerly on startup.
* The {@link SmartFactoryBean} interface allows for exposing
* more fine-grained behavioral metadata.
*
* <p>This interface is heavily used within the framework itself, for
* example for the AOP {@link org.springframework.aop.framework.ProxyFactoryBean}
* or the {@link org.springframework.jndi.JndiObjectFactoryBean}.
* It can be used for application components as well; however,
* this is not common outside of infrastructure code.
*
* <p><b>NOTE:</b> FactoryBean objects participate in the containing
* BeanFactory's synchronization of bean creation. There is usually no
* need for internal synchronization other than for purposes of lazy
* initialization within the FactoryBean itself (or the like). */
public interface FactoryBean<T> {}
所以通过beanName找到了ProxyFactoryBean,然而不是返回该对象,而是返回他的getObject方法的返回值,所以我们通过ProxyFactoryBean的id就可以获取到它所产生的代理对象,所以更改如下:
<bean id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
@Resource(name="aServiceImplProxy")
private AService aService;
然后就可以正常运行了如下:
run my before advice
AServiceImpl.barA()
源代码解读
然后我们就要源码分析下这一过程,先看下是如何产生代理对象的,在ProxyFactoryBean的getObject方法中:
public class ProxyFactoryBean extends ProxyCreatorSupport
implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
@Override
public Object getObject() throws BeansException {
//重点一
initializeAdvisorChain();
if (isSingleton()) {
//重点二
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
}
重点1:就是根据我们配置的interceptorNames来获取对应的bean,并却转化成Advisor。
this.advisorChainInitialized:标示是否已进行过初始化,若以初始化则不再进行初始化。然后就是将interceptorNames转化成Advisor。根据interceptorNames所包含的字符串到容器中进行查找,如果含有*则,则表示进行一定的匹配,符合的都会纳入。
如下:
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
}
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}
// Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
}
// Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}
this.advisorChainInitialized = true;
}
这中间页经过了Advice到Advisor的转换,如下:
private void addAdvisorOnChainCreation(Object next, String name) {
// We need to convert to an Advisor if necessary so that our source reference
// matches what we find from superclass interceptors.
Advisor advisor = namedBeanToAdvisor(next);
if (logger.isTraceEnabled()) {
logger.trace("Adding advisor with name '" + name + "'");
}
addAdvisor(advisor);
}
private Advisor namedBeanToAdvisor(Object next) {
try {
return this.advisorAdapterRegistry.wrap(next);
}
catch (UnknownAdviceTypeException ex) {
// We expected this to be an Advisor or Advice,
// but it wasn't. This is a configuration error.
throw new AopConfigException("Unknown advisor type " + next.getClass() +
"; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," +
"which may also be target or TargetSource", ex);
}
}
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
}
这个包裹过程已经见过很多遍了,采用了适配器的模式。
之后又是和其他的AOP方式接轨了,设置一些列要实现的接口和参数,使用DefaultAopProxyFactory先创建出AopProxy,要么是JdkDynamicAopProxy,要么是CglibAopProxy,然后就可以调用AopProxy的getProxy方法来获取代理对象了
这种方式实现的AOP还是比较麻烦的,同时配置一个ProxyFactoryBean仅能实现对一个目标对象的拦截,要想拦截多个目标对象,需要配置多个ProxyFactoryBean。所以大部分还是使用Spring引进的aspectj的AOP方式来进行AOP编程。
spring---aop(6)---Spring AOP中ProxyFactoryBean介绍的更多相关文章
- Spring源码之Springboot中监听器介绍
https://www.bilibili.com/video/BV12C4y1s7dR?p=11 监听器模式要素 事件 监听器 广播器 触发机制 Springboot中监听模式总结 在SpringAp ...
- Spring框架IOC和AOP介绍
说明:本文部分内容参考其他优秀博客后结合自己实战例子改编如下 Spring框架是个轻量级的Java EE框架.所谓轻量级,是指不依赖于容器就能运行的.Struts.Hibernate也是轻量级的. 轻 ...
- 【spring 5】AOP:spring中对于AOP的的实现
在前两篇博客中,介绍了AOP实现的基础:静态代理和动态代理,这篇博客介绍spring中AOP的实现. 一.采用Annotation方式 首先引入jar包:aspectjrt.jar && ...
- spring aop方式配置事务中的三个概念 pointcut advice advisor
AOP的3个关键概念 因为AOP的概念难于理解,所以在前面首先对Java动态代理机制进行了一下讲解,从而使读者能够循序渐进地来理解AOP的思想. 学习AOP,关键在于理解AOP的思想,能够使用AOP. ...
- JavaWeb_(Spring框架)认识Spring中的aop
1.aop思想介绍(面向切面编程):将纵向重复代码,横向抽取解决,简称:横切 2.Spring中的aop:无需我们自己写动态代理的代码,spring可以将容器中管理对象生成动态代理对象,前提是我们对他 ...
- 你知道Spring是怎么将AOP应用到Bean的生命周期中的吗?
聊一聊Spring是怎么将AOP应用到Bean的生命周期中的? 本系列文章: 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译 读源码,我们可以从第一行读起 你知道Spr ...
- AOP 与 Spring中AOP使用(下)
AOP通知类型 前置通知 在目标方法执行之前进行操作 UserDao.java public class UserDao { public void add(){ System.out.println ...
- AOP 与 Spring中AOP使用(上)
AOP简介 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续 ...
- spring(六):spring中AOP的基本使用
AOP:面向切面编程[底层使用动态代理实现],就是在运行期间动态的将某段代码切入到方法的指定位置进行运行的编程方式 基本使用 使用AOP功能需要引入spring的aop以及aspects相关包 < ...
随机推荐
- 数论-求n以内的质数
一.埃拉托斯特尼筛法 名字很高大上,然而并没有什么卵用…… 思路: 在把<=√n的质数所有的<=n的倍数剔除,剩下的就都是质数了,很容易理解…… 复杂度O(nloglogn) #inclu ...
- OpenStack Benchmark - Rally
作为以基于OpenStack的云平台的基准测试工具 -- Rally, 其功能不仅是测试云的性能&&稳定性, 还可以安装OpenStack,以及以良好的表现形式(web 页面)展现测试 ...
- linux文件管理 -> 系统压缩打包
如果希望windows和Linux互相能使用的压缩工具, 建议.zip格式 压缩的好处主要有: 节省磁盘空间占用率 节省网络传输带宽消耗 网络传输更加快捷 Linux系统常见的后缀名所对应的压缩工具 ...
- python基础--random模块
python使用random生成随机数 下面是主要函数random.random()用于生成一个0到1的随机符点数: 0 <= n < 1.0random.randint(a, b)生成的 ...
- git ——本地项目上传到git
1.(先进入项目文件夹)通过命令 git init 把这个目录变成git可以管理的仓库 git init 2.把文件添加到版本库中,使用命令 git add .添加到暂存区里面去,不要忘记后面的小数点 ...
- TypeScript的配置文件 tsconfig.json
//tsconfig.json指定了用来编译这个项目的根文件和编译选项 { "compilerOptions": { //compilerOptions:编译选项,可以被忽略,这时 ...
- 【LOJ】#2056. 「TJOI / HEOI2016」序列
题解 这个我们处理出来每一位能变化到的最大值和最小值,包括自身 然后我们发现 \(f[i] = max(f[i],f[j] + 1) (mx[j] <= a[i] && a[j] ...
- Loadrunner11在win7下录制脚本,ie打不开
Loadrunner11在win7下录制脚本,ie打不开 使用loadrunner11录制脚本时试了很多办法都无法打开ie浏览器,最后终于解决了 1.ie浏览器去掉启用第三方浏览器扩展 2.loadr ...
- scala递归实现换零钱算法
import scala.collection.mutable.ArrayBuffer import scala.util.control.Breaks object Exchange { def d ...
- python之路【第十二篇】: MYSQL
一. 概述 Mysql是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的RDBMS(Relational Database Management System:关系数据库管理系统)应用 ...