环境:Spring5.2.14
在不修改业务代码的情况下如何让某个类具有某项功能呢,比如具有XXXDAO接口的能力?
1 IntroductionAdvisor介绍
IntroductionAdvisor与PointcutAdvisor区别
- IntroductionAdvisor只能应用于类级别
- IntroductionAdvisor只能应用于Introduction类型的通知,而PointcutAdvisor可以应用于所有类型的通知
- IntroductionAdvisor的Advice需要实现目标的接口,而pintcutAdvisor中的Advice没有改要求
2 IntroductionAdvisor使用流程
假定我们的业务类CustomDAO希望具有DesignDAO(接口)的能力
2.1 目标接口
- public interface DesignDAO {
- public void design() ;
- }
2.2 Introduction拦截器
- public class CustomIntroductionInterceptor implements IntroductionInterceptor, DesignDAO {
- // 判断当前的拦截器是否实现了目标接口(DesignDAO,我们需要某个类具有指定接口的功能)
- @Override
- public boolean implementsInterface(Class<?> intf) {
- return intf.isAssignableFrom(this.getClass()) ;
- }
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- System.out.println("我是通知类:IntroductionInterceptor") ;
- // 判断当前执行的方法所属类是否实现了目标接口
- // 这里必须要进行相应的判断拦截,否则会在没有被拦截的类方法执行的时候报错我
- // 因为你的其它类所对应的接口并没有在该拦截器中被实现。
- if (implementsInterface(invocation.getMethod().getDeclaringClass())) {
- return invocation.getMethod().invoke(this, invocation.getArguments()) ;
- }
- return invocation.proceed() ;
- }
- @Override
- public void design() {
- System.out.println("接口实现了") ;
- }
- }
2.3 IntroductionAdvisor定义
- @Component
- public class CustomIntroductionAdvisor implements IntroductionAdvisor {
- // 定义Advice通知
- @Override
- public Advice getAdvice() {
- return new CustomIntroductionInterceptor() ;
- }
- // 该方法没有被使用,建议直接返回true
- @Override
- public boolean isPerInstance() {
- return true ;
- }
- // 定义了所要实现的所有接口
- @Override
- public Class<?>[] getInterfaces() {
- return new Class<?>[] {DesignDAO.class} ;
- }
- // 过滤类,返回true就会被匹配
- @Override
- public ClassFilter getClassFilter() {
- return new ClassFilter() {
- @Override
- public boolean matches(Class<?> clazz) {
- return CustomDAO.class.isAssignableFrom(clazz) ;
- }
- } ;
- }
- // 在这里我们可以校验我们的Advice类是否实现了执行的接口getInterfaces中定义的接口
- @Override
- public void validateInterfaces() throws IllegalArgumentException {
- // 这里可以参考DefaultIntroductionAdvisor的实现
- }
- }
这里可以查看示例文档37示例源码是如何执行的
2.4 验证
- @Component
- public class CustomerDAOImpl implements CustomDAO {
- public void update() {
- System.out.println("更新数据..." + this.getClass()) ;
- }
- public void save() {
- System.out.println("保存方法..." + this.getClass()) ;
- }
- }
业务类并没有实现DesignDAO接口。接下来看看通过上面定义IntroductionAdvisor是否可以让我们的业务类具有目标类的功能
- public class LauncherMain {
- public static void main(String[] args) {
- System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true") ;
- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.pack") ;
- CustomDAO dao = ctx.getBean(CustomDAO.class) ;
- System.out.println(dao.getClass()) ;
- System.out.println(Arrays.toString(dao.getClass().getInterfaces())) ;
- dao.save() ;
- dao.update() ;
- if (DesignDAO.class.isAssignableFrom(dao.getClass())) {
- DesignDAO ddao = (DesignDAO) dao ;
- System.out.println("IntroductionAdvisor start...") ;
- ddao.design() ;
- System.out.println("IntroductionAdvisor end...") ;
- }
- ctx.close() ;
- }
- }
执行结果:
- class com.sun.proxy.$Proxy14
- [interface com.pack.dao.CustomDAO, interface com.pack.interfaces.DesignDAO, interface org.springframework.aop.SpringProxy, interface org.springframework.aop.framework.Advised, interface org.springframework.core.DecoratingProxy]
- 我是通知类:IntroductionInterceptor, interface com.pack.dao.CustomDAO
- intf: interface com.pack.dao.CustomDAO
- 我被调用了...
- 保存方法...class com.pack.dao.CustomerDAOImpl
- 我是通知类:IntroductionInterceptor, interface com.pack.dao.CustomDAO
- intf: interface com.pack.dao.CustomDAO
- 更新数据...class com.pack.dao.CustomerDAOImpl
- IntroductionAdvisor start...
- 我是通知类:IntroductionInterceptor, interface com.pack.interfaces.DesignDAO
- intf: interface com.pack.interfaces.DesignDAO
- 接口实现了
- IntroductionAdvisor end...