首页 > 其他分享 >Spring AOP官方文档学习笔记(四)之Spring AOP的其他知识点

Spring AOP官方文档学习笔记(四)之Spring AOP的其他知识点

时间:2023-05-09 16:23:09浏览次数:35  
标签:知识点 ExampleA Spring 代理 ... AOP public

1.选择哪种AOP

(1) 使用Spring AOP比使用完整版的AspectJ更方便简单,因为不需要在开发和构建过程中引入AspectJ编译器以及织入器,如果我们只希望通知能够在Spring Bean上执行,那么选用Spring AOP就可以了,如果我们希望通知能够在不由Spring所管理的对象上执行,那么就需要使用AspectJ,如果我们希望为除方法以外的连接点(比如成员变量)提供通知,那么也需要使用AspectJ

2.Spring AOP的代理机制

(1) Spring AOP使用Jdk动态代理或Cglib动态代理来为目标对象创建代理对象,Jdk动态代理由Jdk提供,而Cglib动态代理则是由一个开源类库提供,如果要代理的目标对象至少实现了一个接口,那么就会使用Jdk动态代理,否则如果目标对象没有实现任何接口,那么就会使用Cglib动态代理,创建一个Cglib代理对象

(2) Spring默认使用Jdk动态代理,但我们可以强制让Spring始终使用Cglib动态代理,但需注意,使用Cglib动态代理,无法对final修饰的方法织入通知,因为这些方法不能在子类中被重写,具体开启Cglib动态代理的方式如下

<!-- 方式一:在使用基于xml的配置时,设置<aop:config/>标签中的proxy-target-class属性为true -->
<aop:config proxy-target-class="true">
        <!-- ... -->
</aop:config>

<!-- 方式二:在混合使用基于xml和注解的配置时,设置<aop:aspectj-autoproxy/>标签中的proxy-target-class属性为true -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 方式三:在使用基于注解的配置时,设置@EnableAspectJAutoProxy注解中的proxyTargetClass属性为true -->
@EnableAspectJAutoProxy(proxyTargetClass = true)

(3) 当一个bean被代理后,我们从容器中获取到这个bean,并对其使用 .getClass().getName() 方法来输出它的类名称,可见如 cn.example.spring.boke.ExampleA$$EnhancerBySpringCGLIB$$ff6c22d2或com.sun.proxy.$Proxy18 这样的输出,而当我们关闭掉AOP后,得到的通常是形如 cn.example.spring.boke.ExampleA 这样的输出,这其实是因为我们从容器中获取的是该bean被增强过后的代理对象,而非它原始的目标对象,因而,对这个bean的方法调用就是对代理对象的方法调用,然后由代理对象委托调用原始对象上相关的方法以及该方法相关的拦截器(advice),如下

(4) 在目标对象中,使用this指针进行自调用不会触发通知的执行

//一个普通的bean,在它的a方法中使用this指针,自调用b方法
@Component
public class ExampleA{
    public void a() {
        System.out.println("a...");
        this.b();
    }

    public void b() {
        System.out.println("b...");
    }
}

//切面,切入ExampleA这个bean中的所有方法
@Component
@Aspect
public class Logger {
    @Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
    public void beforePrint() {
        System.out.println("beforePrint...");
    }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config { }

//获取ExampleA,调用a方法,打印结果
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
ExampleA exampleA = ctx.getBean(ExampleA.class);
exampleA.a();

//可见,Spring对a方法进行了织入,而b方法却没有,原因就是因为这里的this指向的是目标对象,一个普通的bean ExampleA,而非它的代理对象,自然而然无法进行织入了,因此关键的目标就是如何获取到代理对象
beforePrint...
a...
b...


//想要获取代理对象,首先要先将@EnableAspectJAutoProxy注解中的exposeProxy属性设置为true
@EnableAspectJAutoProxy(exposeProxy = true)

//接着修改ExampleA中的a方法,在调用b方法时不再使用this指针,而是AopContext.currentProxy(),即获取当前对象的代理对象
public void a() {
    System.out.println("a...");
//        this.b();
    ((ExampleA) AopContext.currentProxy()).b();
}

//接着,再执行打印,可见此时通知已被正确执行
beforePrint...
a...
beforePrint...
b...

Spring不推荐使用如上的方法,因为这会使Spring AOP与我们的代码强耦合,具有侵入性,最好的方式是重构我们的代码,避免发生自调用,此外,Spring AOP会产生这种问题的原因是Spring AOP是基于代理实现的,而AspectJ框架就不存在这种自调用问题,因为它不是一个基于代理的AOP框架

3.基于java代码的AOP

public class ExampleA{
    public void a() {
        System.out.println("a...");
    }
}

//切面必须由@Aspect注解标注,否则容器会抛出异常
@Aspect
public class Logger {
    @Before(value = "execution(* cn.example.spring.boke.ExampleA.*(..))")
    public void beforePrint() {
        System.out.println("beforePrint...");
    }
}

//创建AOP工厂,生成代理对象
public static void main(String[] args) throws Exception {
    //1.使用AspectJProxyFactory工厂,用于生成目标对象的代理对象
    AspectJProxyFactory factory = new AspectJProxyFactory(new ExampleA());

    //2.添加一个切面,该切面必须由@Aspect注解标注
    factory.addAspect(Logger.class);

    //3.生成代理对象
    ExampleA proxy = factory.getProxy();
    proxy.a();
}

标签:知识点,ExampleA,Spring,代理,...,AOP,public
From: https://www.cnblogs.com/shame11/p/17370702.html

相关文章

  • spring框架_Applicationcontext功能
    Applicationcontext的功能拓展主要来自于不属于beanfactory的接口,主要包括四个接口Messagesource:国际化ResourcePatternResolver:获取资源ApplicationEventPublisher:发布事件EnvironmentCapable:获取环境变量感觉发布事件这个功能有点像消息队列,发布订阅,在compent......
  • SpringMVC常用注解整理
    一、组件型注解:@Component在类定义之前添加@Component注解,他会被spring容器识别,并转为bean。@Repository对Dao实现类进行注解(特殊的@Component)@Service用于对业务逻辑层进行注解,(特殊的@Component)@Controller用于控制层注解,(特殊的@Component)以上四种注解都是......
  • SpringBoot - 参数接收方式
    SpringBoot-参数接收方式·前言·使用@PathVariable接收路径中的参数·使用@RequestParam获取路径中?后的参数·使用@RequestBody获取Map对象·使用@RequestBody获取实体对象前言使用@PathVariable接收路径中的参数@GetMapping(value="/param/{id}")publicStri......
  • springBootMVC搭建
    springBootMVC搭建 分类专栏:spring环境配置spring环境配置专栏收录该内容17篇文章0订阅订阅专栏今天给大家介绍一下springBootMVC,让我们学习一下如何利用SpringBoot快速的搭建一个简单的web应用。环境准备一个称手的文本编辑器(例如Vim、Emacs、SublimeText)或者I......
  • 注解驱动的spring mvc(二)
    四:视图名的确定。springmvc可以通过可以通过多种方式确定视图名,在前面的例子中,方法无返回值,视图名更具请求参数确定。Controller方法还是返回一个String类型的值作为视图名。Java代码@RequestMapping("/user/list.htm")publicStringlistAllUser(){ return"user......
  • Spring Boot项目对接腾讯云COS对象存储上传文件接口
    SpringBoot项目对接腾讯云COS对象存储上传文件接口pom.xml<dependency><groupId>com.qcloud</groupId><artifactId>cos_api</artifactId><version>5.6.35</version></dependency>application.ymlcos:secretId:xxx......
  • Spring Boot 单体应用一键升级成 Spring Cloud Alibaba
    作者:十眠背景随着ApacheDubbo、Nacos以及SpringCloud等服务框架的流行,越来越多的企业开始采用微服务架构来构建其应用程序。微服务架构使企业能够将其应用程序拆分成多个小型服务,这些服务可以独立部署和扩展。这种架构模式也使企业更容易实现敏捷开发和持续交付,从而提高了......
  • WPF知识点全攻略15- 线程处理
    使用WPF构建应用程序时,想要保证系统的流畅性、用户的体验性,处理好UI线程(主线程)与其他线程(子线程)的关系是必要的。以最近大火的直播带货为例,镜头前主播(部分副播)的语言动作是主线程,镜头外的场控、客服等人员,各自都有一个属于自己的子线程。场控在做软硬件调试、商品上架下架、发优......
  • SpringBoot的@Configuration注解
    本文主要讲述SpringBoot的@Configuration注解。一.POJO类的声明例如有两个pojo类,分别是User和PetUser类的声明如下:publicclassUser{privateStringname;privateIntegerage;publicUser(){}publicUser(Stringname,Integer......
  • Spring注解开发报错
    今天学习Spring注解开发时,又报错了报错代码Exceptioninthread“main”org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:Line6inXMLdocumentfromclasspathresource[1.xml]isinvalid;nestedexceptionisorg.xml.sax.SAXParseExceptio......