文章目录
- 1、Spring AOP概述
- 2、Spring AOP术语
- 3、增强器的一种:IntroductionAdvisor
- 4、Spring AOP API
- 5、Spring AOP底层原理-动态代理
- 6、如何查看动态生成的代理类源码?
本文标题1-5会用到标题6的知识:关于如何查看动态生成的代理类的源码的相关介绍,有兴趣可以看一下短视频!
1、Spring AOP概述
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、增强器的一种:IntroductionAdvisor
IntroductionAdvisor和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$$73ed774c 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$$550bae40 extends MailBean implements IMailService, SpringProxy, Advised, Factory
4、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 IProductService
2、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/