}
需求
在spring容器中注册上面这个类的bean,名称为
service1
,通过代理的方式来对这个bean进行增强,来2个通知
一个前置通知:在调用
service1
中的任意方法之前,输出一条信息:准备调用xxxx方法
一个环绕通知:复制统计所有方法的耗时。
下面是代码的实现
package com.javacode2018.aop.demo8.test1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
@Configuration
public class MainConfig1 {
//注册目标对象
@Bean
public Service1 service1() {
return new Service1();
}
//注册一个前置通知
@Bean
public MethodBeforeAdvice beforeAdvice() {
MethodBeforeAdvice advice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println(“准备调用:” + method);
}
};
return advice;
}
//注册一个后置通知
@Bean
public MethodInterceptor costTimeInterceptor() {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long starTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();
System.out.println(invocation.getMethod() + “,耗时(纳秒):” + (endTime - starTime));
return result;
}
};
return methodInterceptor;
}
//注册ProxyFactoryBean
@Bean
public ProxyFactoryBean service1Proxy() {
//1.创建ProxyFactoryBean
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//2.设置目标对象的bean名称
proxyFactoryBean.setTargetName(“service1”);
//3.设置拦截器的bean名称列表,此处2个(advice1和advice2)
proxyFactoryBean.setInterceptorNames(“beforeAdvice”, “costTimeInterceptor”);
return proxyFactoryBean;
}
}
下面启动spring容器,并获取代理对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig1.class);
//获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy
Service1 bean = context.getBean(“service1Proxy”, Service1.class);
System.out.println(“----------------------”);
//调用代理的方法
bean.m1();
System.out.println(“----------------------”);
//调用代理的方法
bean.m2();
运行输出
准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
我是 m1 方法
public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒):8680400
准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
我是 m2 方法
public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒):82400
从输出中可以看到,目标对象service1
已经被增强了。
ProxyFactoryBean中的interceptorNames
interceptorNames
用来指定拦截器的bean名称列表,常用的2种方式。
-
批量的方式
-
非批量的方式
批量的方式
使用方法
proxyFactoryBean.setInterceptorNames(“需要匹配的bean名称*”);
需要匹配的bean名称后面跟上一个*
,可以用来批量的匹配,如:interceptor*
,此时spring会从容器中找到下面2中类型的所有bean,bean名称以interceptor
开头的将作为增强器
org.springframework.aop.Advisor
org.aopalliance.intercept.Interceptor
这个地方使用的时候需要注意,批量的方式注册的时候,如果增强器的类型不是上面2种类型的,比如下面3种类型通知,我们需要将其包装为Advisor
才可以,而MethodInterceptor
是Interceptor
类型的,可以不用包装为Advisor
类型的。
MethodBeforeAdvice(方法前置通知)
AfterReturningAdvice(方法后置通知)
ThrowsAdvice(异常通知)
下面来个案例感受一下。
案例
下面批量注册2个增强器。
package com.javacode2018.aop.demo8.test2;
import com.javacode2018.aop.demo8.test1.Service1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
public class MainConfig2 {
//注册目标对象
@Bean
public Service1 service1() {
return new Service1();
}
//定义一个增强器:interceptor1,内部是一个前置通知,需要将其包装为Advisor类型的
@Bean
public Advisor interceptor1() {
MethodBeforeAdvice advice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println(“准备调用:” + method);
}
};
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setAdvice(advice);
return advisor;
}
//定义一个增强器:interceptor2
@Bean
public MethodInterceptor interceptor2() {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long starTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();
System.out.println(invocation.getMethod() + “,耗时(纳秒):” + (endTime - starTime));
return result;
}
};
return methodInterceptor;
}
//注册ProxyFactoryBean
@Bean
public ProxyFactoryBean service1Proxy() {
//1.创建ProxyFactoryBean
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//2.设置目标对象的bean名称
proxyFactoryBean.setTargetName(“service1”);
//3.设置拦截器的bean名称列表,此处批量注册
proxyFactoryBean.setInterceptorNames(“interceptor*”); //@1
return proxyFactoryBean;
}
}
上面定义了2个增强器:
interceptor1:前置通知,包装为Advisor类型了
interceptor2:环绕通知,MethodInterceptor类型的
测试代码
@Test
public void test2() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
//获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy
Service1 bean = context.getBean(“service1Proxy”, Service1.class);
System.out.println(“----------------------”);
//调用代理的方法
bean.m1();
System.out.println(“----------------------”);
//调用代理的方法
bean.m2();
}
运行输出
准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
我是 m1 方法
public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒):10326200
准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
我是 m2 方法
public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒):52000
非批量的方式
用法
非批量的方式,需要注册多个增强器,需明确的指定多个增强器的bean名称,多个增强器按照参数中指定的顺序执行,如
proxyFactoryBean.setInterceptorNames(“advice1”,“advice2”);
advice1、advice2
对应的bean类型必须为下面列表中指定的类型,类型这块比匹配的方式范围广一些
MethodBeforeAdvice(方法前置通知)
AfterReturningAdvice(方法后置通知)
ThrowsAdvice(异常通知)
org.aopalliance.intercept.MethodInterceptor(环绕通知)
org.springframework.aop.Advisor(顾问)
下面来个案例。
案例
这次给service1
来3个通知:前置、环绕、后置
package com.javacode2018.aop.demo8.test3;
import com.javacode2018.aop.demo8.test1.Service1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
public class MainConfig3 {
//注册目标对象
@Bean
public Service1 service1() {
return new Service1();
}
//定义一个前置通知
@Bean
public MethodBeforeAdvice methodBeforeAdvice() {
MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println(“准备调用:” + method);
}
};
return methodBeforeAdvice;
}
//定义一个环绕通知
@Bean
public MethodInterceptor methodInterceptor() {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long starTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();
System.out.println(invocation.getMethod() + “,耗时(纳秒):” + (endTime - starTime));
return result;
}
};
return methodInterceptor;
}
//定义一个后置通知
@Bean
public AfterReturningAdvice afterReturningAdvice() {
AfterReturningAdvice afterReturningAdvice = new AfterReturningAdvice() {
@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println(method + “,执行完毕!”);
}
};
return afterReturningAdvice;
}
//注册ProxyFactoryBean
@Bean
public ProxyFactoryBean service1Proxy() {
//1.创建ProxyFactoryBean
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//2.设置目标对象的bean名称
proxyFactoryBean.setTargetName(“service1”);
//3.设置拦截器的bean名称列表,此处批量注册
proxyFactoryBean.setInterceptorNames(“methodBeforeAdvice”, “methodInterceptor”, “afterReturningAdvice”);
return proxyFactoryBean;
}
}
测试代码
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
//获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy
Service1 bean = context.getBean(“service1Proxy”, Service1.class);
System.out.println(“----------------------”);
//调用代理的方法
bean.m1();
System.out.println(“----------------------”);
//调用代理的方法
bean.m2();
}
运行输出
准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
我是 m1 方法
public void com.javacode2018.aop.demo8.test1.Service1.m1(),执行完毕!
public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒):12724100
准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
我是 m2 方法
public void com.javacode2018.aop.demo8.test1.Service1.m2(),执行完毕!
public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒):76700
源码解析
重点在于下面这个方法
org.springframework.aop.framework.ProxyFactoryBean#getObject
源码:
public Object getObject() throws BeansException {
//初始化advisor(拦截器)链
initializeAdvisorChain();
//是否是单例
if (isSingleton()) {
//创建单例代理对象
return getSingletonInstance();
}
else {
//创建多例代理对象
return newPrototypeInstance();
}
}
initializeAdvisorChain
方法,用来初始化advisor(拦截器)链,是根据interceptorNames
配置,找到spring容器中符合条件的拦截器,将其放入创建aop代理的配置中
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
// 轮询 interceptorNames
for (String name : this.interceptorNames) {
//批量注册的方式:判断name是否以*结尾
if (name.endsWith(GLOBAL_SUFFIX)) {
//@1:从容器中匹配查找匹配的增强器,将其添加到aop配置中
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
//非匹配的方式:按照name查找bean,将其包装为Advisor丢到aop配置中
Object advice;
//从容器中查找bean
advice = this.beanFactory.getBean(name);
//@2:将advice添加到拦截器列表中
addAdvisorOnChainCreation(advice, name);
}
}
}
}
@1:addGlobalAdvisor
批量的方式Advisor,看一下源码,比较简单
/**
* 添加所有全局拦截器和切入点,
* 容器中所有类型为Advisor/Interceptor的bean,bean名称prefix开头的都会将其添加到拦截器链中
*/
private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {
//获取容器中所有类型为Advisor的bean
String[] globalAdvisorNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class);
//获取容器中所有类型为Interceptor的bean
String[] globalInterceptorNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class);
List beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length);
Map<Object, String> names = new HashMap<>(beans.size());
for (String name : globalAdvisorNames) {
Object bean = beanFactory.getBean(name);
beans.add(bean);
names.put(bean, name);
}
for (String name : globalInterceptorNames) {
Object bean = beanFactory.getBean(name);
beans.add(bean);
names.put(bean, name);
}
//对beans进行排序,可以实现Ordered接口,排序规则:order asc
AnnotationAwareOrderComparator.sort(beans);
for (Object bean : beans) {
String name = names.get(bean);
//判断bean是否已prefix开头
if (name.startsWith(prefix)) {
//将其添加到拦截器链中
addAdvisorOnChainCreation(bean, name);
}
}
}
@2:addAdvisorOnChainCreation
private void addAdvisorOnChainCreation(Object next, String name) {
//namedBeanToAdvisor用来将bean转换为advisor
Advisor advisor = namedBeanToAdvisor(next);
//将advisor添加到拦截器链中
addAdvisor(advisor);
}
namedBeanToAdvisor
方法
private AdvisorAdapterRegistry advisorAdapterRegistry = new DefaultAdvisorAdapterRegistry();
private Advisor namedBeanToAdvisor(Object next) {
//将对象包装为Advisor对象
return this.advisorAdapterRegistry.wrap(next);
}
对AdvisorAdapterRegistry
不清楚的,看一下上一篇文章:Spring系列第32篇:AOP核心源码、原理详解
advisorAdapterRegistry#wrap
方法将adviceObject
包装为Advisor
对象,代码如下,比较简单
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?
掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-RSpkoaWY-1712871999904)]
[外链图片转存中…(img-p95nrL36-1712871999905)]
[外链图片转存中…(img-6tj0awbi-1712871999905)]
[外链图片转存中…(img-0NhyVoif-1712871999906)]
[外链图片转存中…(img-TJn5y8j4-1712871999906)]
[外链图片转存中…(img-YofgjcH4-1712871999906)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-0krcyUA0-1712871999906)]
最后
面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?
掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。
[外链图片转存中…(img-aW20VMq2-1712871999907)]
[外链图片转存中…(img-pqZ42h0n-1712871999907)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-YI7ifv1q-1712871999907)]