1、Spring AOP概述2、Spring AOP术语3、增强器的一种:IntroductionAdvisor4、Spring AOP API
1、使用编程的方式基于AspectJ创建代理2、顶层接口3、拦截和通知接口4、使用编程的方式创建代理5、自动代理(auto-proxy) 5、Spring AOP底层原理-动态代理
1、JDK动态代理2、Cglib动态代理
6、如何查看动态生成的代理类源码?
本文标题1-5会用到标题6的知识:关于如何查看动态生成的代理类的源码的相关介绍,有兴趣可以看一下短视频!
AOP,面向切面编程,是一种编程思想,是抽象,而AspectJ和Spring AOP这两个框架分别实现了AOP。
Aspect是更通用的AOP框架,像其它语言也有对应的实现,如AspectC、AspectC++、Aspect.Net、AspectPHP等。
Spring AOP中引入AspectJ仅用于定义切面(基于@AspectJ的AOP支持),但运行时仍然是纯 Spring AOP,并且不依赖于 AspectJ 编织器(ajc) 。
Advice和Advisor的区别:Advice是通知(如before通知、after通知等),Advisor 是通知器(增强器),Advisor接口源码如下所示,由此可见Advisor和Advice是一对一关系。
package org.springframework.aop; import org.aopalliance.aop.Advice; public interface Advisor { Advice EMPTY_ADVICE = new Advice() {}; Advice getAdvice(); boolean isPerInstance(); }
Tips:
切入点匹配的连接点的概念是 AOP 的关键 。Spring Aop仅支持方法拦截,不支持字段拦截Spring Aop目的不是提供最完整的 AOP 实现(尽管 Spring AOP 非常有能力),而是提供 AOP 实现和 Spring IoC 之间的紧密集成,以帮助解决企业应用程序中的常见问题jdk动态代理仅支持public方法,cglib虽然支持public、protected和package方法,但代理方法最好是public的 2、Spring AOP术语
切面(Aspect ):有两种定义切面的方式:基于xml和基于@AspectJ
连接点(Join point):程序执行过程中的一个点,例如方法的执行或异常的处理。在 Spring AOP中,一个连接点总是代表一个方法执行
通知(Advice):切面在特定连接点上采取的 *** 作
PointCut(切入点):匹配连接点的谓词。Advice 与切入点表达式相关联,并在与切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。Spring 默认使用 AspectJ 切入点表达式语言
目标对象
代理对象
编织(Weaving):可以在编译时(需要特殊的编译器,如AspectJ的ajc编译器)、类加载时(需要特殊的类加载器,如AspectJ5的加载时织入load-time weaving,LTW)和运行时完成(动态代理)
通知类型
before:方法执行前(不能出异常)
After returning :方法返回前运行(不能出异常)
After throwing:如果方法因抛出异常而退出,则运行
After(最终):方法正常运行完或抛出异常,都将运行
Around:这是最一般的建议,功能更强大。Spring官方建议尽量不要使用Around通知,例如将方法返回值做缓存,应优先选择After returning而不是Around
3、增强器的一种:IntroductionAdvisorIntroductionAdvisor和PointcutAdvisor一切,都是Advisor接口的子类,都是增强器,下面通过代码看看Introduction是如何使用的。
@Component("mailBean") public class MailBean { public void sayHello(){ System.out.println("--MailBean hello--"); } } public interface IMailService { void send(); } public class DefaultMailServiceImpl implements IMailService { @Override public void send() { System.out.println("-- MailServiceImp::send--"); } } @Aspect @Component public class IntroductionAspect { // 重点在这里,这里会通过动态代理生成一个bean,该bean继承自MailBean并实现了IMailService接口,相当于给MailBean增加了一个send方法 // 当执行send方法时,会被Introduction Advice拦截到,并执行DefaultMailServiceImpl的send方法 @DeclareParents(value = "com.bobo.springbootdemo.introduction.MailBean", defaultImpl = DefaultMailServiceImpl.class) private IMailService iMailService; } @SpringBootTest @RunWith(SpringRunner.class) public class IntroductionTests { @Autowired private ApplicationContext context; @Test public void test(){ IMailService mailService = context.getBean("mailBean", IMailService.class); MailBean mailBean = context.getBean("mailBean", MailBean.class); mailService.send(); // 输出-- MailServiceImp::send-- mailBean.sayHello();// 输出--MailBean hello-- } } // 动态生成的代理类声明如下 public class MailBean$$EnhancerBySpringCGLIB$ed774c extends MailBean implements IMailService, SpringProxy, Advised, Factory
最后说一下Spring官方的介绍:introduction代表为一个类添加的新字段或方法定义,Spring AOP允许我们为任何对象introduce(引入)新的接口实现,由此一目了然。
public interface IntroductionAdvisor extends Advisor, IntroductionInfo { // 不需要MethodMatcher,也不需要PointCut,只要ClassFilter过滤出符合的类即可 ClassFilter getClassFilter(); void validateInterfaces() throws IllegalArgumentException; }
如何将IntroductionAdvisor和IntroductionInterceptor关联在一起呢?这里以DefaultIntroductionAdvisor和DelegatingIntroductionInterceptor为例,以编程的方式实现DeclareParents注解同等功能。
public interface IMailService { void send(); } public class MailBean { public void sayHello(){ System.out.println("--MailBean hello--"); } } public class IntroductionApiInterceptor extends DelegatingIntroductionInterceptor implements IMailService{ @Override public Object invoke(MethodInvocation mi) throws Throwable { // return mi.proceed(); // 不能这样用,因为原来的类MailBean没有send方法 return super.invoke(mi); // 执行原来的类MailBean没有的send方法,这就是引入(Introduction) } @Override public void send() { System.out.println("-- IntroductionApiInterceptor::send--"); } } public class IntroductionApiAdvisor extends DefaultIntroductionAdvisor { public IntroductionApiAdvisor(){ super(new IntroductionApiInterceptor(), IMailService.class); } } @Test public void test(){ ProxyFactory factory = new ProxyFactory(new MailBean()); factory.setProxyTargetClass(true); factory.addAdvisors(new IntroductionApiAdvisor()); MailBean mailBean = (MailBean)factory.getProxy(); IMailService mailService = (IMailService)proxy; mailBean.sayHello(); // 输出--MailBean hello-- mailService.send(); // 输出-- MailServiceImp::send-- } // 动态生成的代理类声明如下 public class MailBean$$EnhancerBySpringCGLIB$0bae40 extends MailBean implements IMailService, SpringProxy, Advised, Factory4、Spring AOP API 1、使用编程的方式基于AspectJ创建代理
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject); // SecurityManager类必须打了@Aspect注解 factory.addAspect(SecurityManager.class); // 也可以用一个实例,但是也要打了@Aspect注解 factory.addAspect(usageTracker); // 生成代理对象 MyInterfaceType proxy = factory.getProxy();2、顶层接口
// 增强器 public interface Advisor { Advice EMPTY_ADVICE = new Advice() {}; Advice getAdvice(); boolean isPerInstance(); } // 通知 public interface Advice { } // 切入点增强器 public interface PointcutAdvisor extends Advisor { Pointcut getPointcut(); } // 切入点 public interface Pointcut { // ClassFilter接口用于将切入点限制为给定的一组目标类 ClassFilter getClassFilter(); // 通常比ClassFilter更重要 MethodMatcher getMethodMatcher(); Pointcut TRUE = TruePointcut.INSTANCE; } public interface MethodMatcher { // 静态切入点只需要调用2参数的matches方法 boolean matches(Method m, Class> targetClass); boolean isRuntime(); // 对于动态切入点,当2参数matches方法和isRuntime都返回true时,每次执行方法都要调用一下3参数的matches方法(因为要考虑方法参数) boolean matches(Method m, Class> targetClass, Object... args); }
静态切入点(只考虑类和方法):子类只需要实现StaticMethodMatcherPointcut 即可,如正则表达式切入点:JdkRegexpMethodPointcut。
动态切入点(还要考虑方法参数):子类要实现DynamicMethodMatcherPointcut
3、拦截和通知接口public interface Interceptor extends Advice { } // Around通知 public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable; } public interface BeforeAdvice extends Advice { } // Before通知 public interface MethodBeforeAdvice extends BeforeAdvice { void before(Method m, Object[] args, Object target) throws Throwable; } public interface AfterAdvice extends Advice { } // throw通知,throw通知不需要实现任何方法,因此需要自己添加方法:void afterThrowing(Method m, Object[] args, Object target, ServletException ex) public interface ThrowsAdvice extends AfterAdvice { } // After Return通知 public interface AfterReturningAdvice extends AfterAdvice { void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable; } // Introduction Advice,这是一种特殊的通知。IntroductionInterceptor的用法见2.3节 public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice { }4、使用编程的方式创建代理
ProxyFactory factory = new ProxyFactory(new MailBean()); factory.setProxyTargetClass(true); factory.addAdvisors(new IntroductionApiAdvisor()); MailBean mailBean = (MailBean)factory.getProxy(); // 创建完代理后,仍然可以增删增强器和通知 Advised advised = (Advised) mailBean; Advisor[] advisors = advised.getAdvisors(); advised.addAdvice(new DebugInterceptor()); advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));
ProxyConfig提供的属性:
proxyTargetClass:是否代理目标类
optimize:是否优化,如果不了解优化原理则不要轻易使用
frozen:如果为true则不允许更改配置。如果不希望调用者能够 *** 纵代理(通过Advised 接口)则设为true,默认是false
exposeProxy:确定当前代理是否应该在ThreadLocal公开以便目标可以访问它(通过AopContext.currentProxy())
ProxyFactoryBean提供的属性:
proxyInterfaces:String接口名称数组。如果未提供,则使用目标类的CGLIB 代理interceptorNames:Advisor、interceptor或其它Advice的String数组,这些名称是当前工厂中的 bean 名称,可使用*号singleton :是否是单例,默认值为true
ProxyFactoryBean的例子:
// 定义目标类 @Component public class MessageBean { public void sendMsg(){ System.out.println("-- MessageBean sendMsg --"); } } // 定义前置通知 @Component public class MessageBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("-- MessageBeforeAdvice before --"); } } // 定义增强器,注入前置通知 @Component("messageAdvisor") public class MessageAdvisor extends StaticMethodMatcherPointcutAdvisor { @Autowired private MessageBeforeAdvice messageBeforeAdvice; @PostConstruct public void init(){ super.setAdvice(messageBeforeAdvice); } @Override public boolean matches(Method method, Class> targetClass) { if(targetClass.getName().contains("MessageBean") && method.getName().contains("send")){ return true; } return false; } } // 定义后置通知 @Component("messageAfterAdvice") public class MessageAfterAdvice implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("-- MessageAfterAdvice afterReturning --"); } } // 配置ProxyFactoryBean @Configuration public class ProxyFactoryBeanConfig { @Autowired private MessageBean messageBean; @Bean public ProxyFactoryBean proxyFactoryBean(){ ProxyFactoryBean bean = new ProxyFactoryBean(); bean.setTarget(messageBean); bean.setProxyTargetClass(true); bean.setInterceptorNames("messageAdvisor","messageAfterAdvice"); bean.setSingleton(true); return bean; } } // 单元测试 @Test public void test3(){ ProxyFactoryBean proxyFactoryBean = context.getBean(ProxyFactoryBean.class); MessageBean proxy = (MessageBean)proxyFactoryBean.getObject(); proxy.sendMsg(); } 最后输出 // -- MessageBeforeAdvice before -- // -- MessageBeforeAdvice before -- // -- MessageBean sendMsg -- // -- MessageAfterAdvice afterReturning --5、自动代理(auto-proxy)
前面都是通过ProxyFactory或ProxyFactoryBean来为目标类创建代理,但这种方式虽然灵活,但不利于管理,下面考虑使用自动代理功能。
自动代理建立在BeanPostProcessor的基础上,org.springframework.aop.framework.autoproxy包提供了一些现成的自动代理类
BeanNameAutoProxyCreator:通过beanName通配符匹配,如果匹配成功则创建代理DefaultAdvisorAutoProxyCreator:更加通用和强大 5、Spring AOP底层原理-动态代理 1、JDK动态代理
public interface IProductService { void update(); } public class ProductServiceImpl implements IProductService { @Override public void update() { System.out.println("-- ProductServiceImpl update --"); } } public class ProductHandler implements InvocationHandler { private Object target; public ProductHandler(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before advice"); // 这里必须要传入目标对象 Object res = method.invoke(target,args); System.out.println("after advice"); return res; } } public class JdkDynamicProxyTests { public static void main(String[] args) { Object obj = Proxy.newProxyInstance(JdkDynamicProxyTests.class.getClassLoader(), new Class[]{IProductService.class}, new ProductHandler(new ProductServiceImpl())); IProductService productService = (IProductService)obj; productService.update(); } } // 最后生成的代理类签名如下 // public final class $Proxy0 extends Proxy implements IProductService2、Cglib动态代理
public class ProductServiceImpl implements IProductService { @Override public void update() { System.out.println("-- ProductServiceImpl update --"); } } public class ProductCallback implements MethodInterceptor { // obj:生成的代理对象;method:目标方法;methodProxy:目标方法+代理方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before advice"); Object res = methodProxy.invokeSuper(obj,args); System.out.println("after advice"); return res; } } public class CglibDynamicProxyTests { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ProductServiceImpl.class); enhancer.setCallback(new ProductCallback()); ProductServiceImpl service = (ProductServiceImpl)enhancer.create(); service.update(); } }
注意:要区分org.aopalliance.intercept.MethodInterceptor和org.springframework.cglib.proxy.MethodInterceptor,前者是Advice的子类,是通知,而后者是Callback的子类,用于Cglib生成代理类时被动态织入代理类中。
6、如何查看动态生成的代理类源码?如何查看动态代理类的源码
视频链接:https://www.bilibili.com/video/BV1fb4y1j7LP/
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)