Spring FrameWork
Spring FrameWork
- Spring 简介
- 1.1 Spring 定义
- 1.2 Spring 作用
- 1.3 使用范围
- IOC (Inversion of Control)
- 2.1 接口
- 2.2 面向接口编程
- 2.3 IoC(控制反转)
- 2.4 Spring 注入
- Bean
- 3.1 Bean配置项
- 3.2 Bean的作用域
- 3.3 Bean的生命周期
- 3.4 Bean的自动装配(Autowiring)
- 3.5 Aware
- 3.6 Resource
- 3.7 Bean管理的注解实现
- 3.8 Bean管理的常用注解
- 3.8.1 @Required
- 3.8.2 @Autowired
- 3.8.3 @Qualifier
- 3.8.4 @Bean
- 3.8.5 自定义qualifier注解
- 3.8.6 @Resource
- AOP(Aspect Oriented Programming)
- 4.1 AOP基本概念
- 4.2 Spring中的AOP
- 4.3 Spring中AOP的使用
- 4.3.1 pointcut
- 4.3.2 advice
- 4.3.3 Introductions
- 4.3.4 Advisors
- 4.3.5 Spring AOP API
- 4.4 AspectJ
- 4.4.1 aspect
- 4.4.2 pointcut
- 4.4.3 Before advice
- 4.4.4 Introductions
- 4.4.5 切面实例化模型
1. Spring 简介
1.1 Spring 定义
Spring FrameWork |
---|
Spring Expression Language(SpEL) |
Spring Integration |
Spring Web Flow |
Spring Security |
Spring Data |
Spring Batch |
Spring FrameWork (官方文档十分完善):
- 为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用
是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架:
- 从大小和开销两方面而言Spring都很轻量;
- 通过控制反转(IoC)技术达到松耦合的目的;
- 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性开发;
- 包含并管理应用对象的配置和生命周期,这个意义上是一种容器;
- 将简单的组件配置,组合成为复杂的应用,这个意义上是框架。
1.2 Spring 作用
- 容器;提供了对多种技术的支持(JMS,MQ支持,UnitTest…)
- AOP(事务管理,日志等),提供了众多方便应用的辅助类(JDBC Template等)
- 对主流应用框架(Hibernate,Mybatis)提供了良好的支持
1.3 使用范围
- 构建企业应用(SpringMVC+Spring+Mybatis/Hibernate)
- 单独使用Bean容器(Bean管理)
- 单独使用AOP进行切面处理
- 其他Spring功能(对消息的支持等)
- Web互联网应用
为什么要使用框架?
- 软件系统日趋复杂
- 重用度高,开发效率和质量提高
- 软件设计人员要专注于对领域的了解,使需求分析更充分
- 易于上手,快速解决问题
掌握用法 —> 深入理解 —> 不断实践 —> 反复总结 —> 再次深入理解与实践
2. IOC (Inversion of Control)
2.1 接口
- 用于沟通的中介物的抽象化
- 实体把自己提供给外界的一种抽象化说明,用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式
- 对应Java接口即声明,声明了哪些方法是对外公开提供的
- 在Java8中,接口可以拥有方法体
2.2 面向接口编程
- 结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类
- 接口实现的变动不影响各层间的调用,这一点在公共服务中尤为重要
- “面向接口编程”中的”接口”是用于隐藏具体实现和实现多态性的组件
2.3 IoC(控制反转)
Inversion of Control:
- 控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护(获得依赖对象的过程被反转)
- 获得依赖对象的过程由自身管理变为了由IoC容器主动注入
Dependency Injection(依赖注入):
- 是控制反转的一种实现方式 (由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中)
IoC目的:
- 创建对象并组装对象之间的依赖关系
2.4 Spring 注入
- 在启动Spring容器加载Bean配置时,完成对变量的赋值行为
- 常用的两种注入方式:设值注入,构造注入
- Spring中所有对象都被称为Bean(Bean配置:XML/注解)
Bean容器初始化:
- 两个包:org.springframework.beans; org.springframework.context
- BeanFactory:提供配置结构和基本功能,加载并初始化Bean
ApplicationContext:保存了Bean对象并在Spring中被广泛应用
- 初始化ApplicationContext方式:加载本地文件;Classpath(相对工程的位置);Web应用中依赖Servlet或Listener
- 加载文件方式:FileSystemXmlApplicationContext
- Classpath:ClassPathXmlApplicationContext
Web应用:
- Servlet(org.springframework.web.context.ContextLoaderListener)
- Listener(org.springframework.web.context.ContextLoaderServlet)
3. Bean
3.1 Bean配置项
Bean Configuration |
---|
Id |
Class |
Scope |
Constructor arguments |
Properties |
Autowiring mode |
lazy-initialization mode |
Initialization/destruction method |
3.2 Bean的作用域
- singleton:一个Bean容器中只存在一份(即在某一个IOC容器中是单例的)
- prototype:每次请求(每次使用)创建新的实例,destroy方式不生效
- request:每次http请求创建一个实例且仅在当前request内有效
- session:每次http请求创建一个实例且仅在当前session内有效
- global session:基于portlet的web中有效(portlet定义了global session),如果是在web中,同session
3.3 Bean的生命周期
定义,初始化,使用,销毁
初始化:
- 实现org.springframework.beans.factory.InitializingBean接口
- 重写afterPropertiesSet()
- 配置init-method
销毁:
- 实现org.springframework.beans.factory.DisposableBean接口
- 重写destroy()
- 配置destroy-method
配置全局默认初始化,销毁方法:
在<beans>中配置default-init-method="init",default-destroy-method="destroy"
若以上三种方法同时使用,全局默认初始化,销毁方法将被覆盖不执行,并且重写实现接口的方式将优先于配置文件方式的执行
如果一个Bean只在配置文件中配置了全局默认初始化,销毁方法,其他两种方式没有,那么如果实现了相应的方法则执行,没有实现也不会报错,即默认的有没有对配置文件都没有太大影响;但其余两种方式必须实现相应的方法。
3.4 Bean的自动装配(Autowiring)
- No:不做任何操作
- byname:根据属性名自动装配。此选项将检查容器中所有内容并根据名字查找与属性完全一致的bean,并将其与属性自动装配(id唯一,不能重复)
- byType:如果容器中存在一个与指定属性类型相同的Bean,那么将与该属性自动装配;如果存在多个该类型bean,则会抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生(装配失败)
- Constructor:与byType方式类似,不同处是其应用于构造器参数;如果容器中没有找到与构造器参数类型一致的Bean,则抛出异常
3.5 Aware
Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在被初始化后,可以获取相应的资源;通过实现Aware接口,可以对Spring相应的资源进行操作(慎重);为对Spring进行简单的扩展提供了方便的入口。
- ApplicationContextAware
- BeanNameAware
- ApplicationEventPublisherAware
- BeanClassLoaderAware
- BeanFactoryAware
- BootstrapContextAware
- LoadTimeWeaverAware
- MessageSourceAware
- NotificationPublisherAware
- PortletConfigAware
- PortletContextAware
- ResourceLoaderAware
- ServletConfigAware
- ServletContextAware
3.6 Resource
针对于资源文件的统一接口
- UrlResource:URL对应的资源,根据一个URL地址即可构建
- ClassPathResource:获取类路径下的资源文件
- FileSystemResource:获取文件系统里面的资源
- ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源
- InputStreamResource:针对于输入流封装的资源
- ByteArrayResource:针对于字节数组封装的资源
- ResourceLoader:所有的Application Contexts都实现了
ResourceLoader接口,能够获取Resource实例
3.7 Bean管理的注解实现
Classpath扫描与组件管理:从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用java而不是xml定义bean。(例如:@Configuration,@Bean,@Import,@DependsOn)
- @Component是一个通用注解,可用于任何bean(下述是更有针对性的@Component的子注解)
- @Repository通常用于注解DAO类,即持久层
- @Service通常用于注解Service类,即服务层
- @Controller通常用于注解Controller类,即控制层(MVC)
- 元注解(Meta-annotations):注解的注解;Spring提供的注解可以作为自己的代码生成注解,即元数据注解(元注解可以应用到另一个注解);除了value(),元注解还可以有其他属性允许定制。
类的自动检测与注册Bean:Spring可以自动检测类并注册Bean到ApplicationContext中。
- context:annotation-config/:通过在基于XML的Spring配置相关标签(注意包含上下文命名空间);仅会查找在同一个ApplicationContext中的bean注解。
- 检测这些类并注册相应的Bean:需要context:component-scan(可以扫描类的注解)包含context:annotation-config(完成Bean的注册之后,处理Bean中方法,成员变量的注解),通常在使用前者后,不用再使用后者。
- AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来。
使用过滤器进行自定义扫描:默认情况下,类被自动发现并注册bean的条件是使用@Component,@Repository,@Service,@Controller注解或使用@Component的自定义注解。
<context:component-scan base-package="org.example">
<context:include-filter type="regex" expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
使用注解定义Bean:扫描过程中组件被自动检测,Bean名称是由BeanNameGenerator生成的(@Component,@Repository,@Service,@Controller都会有个name属性用于显式设置Bean Name;如果没有显式设置name属性,则会以注解的类名为基础第一个字符小写来自动生成Bean Name)。
作用域(scope):通常情况下自动查找的Spring组件的scope是singleton,Spring2.5提供了一个标识scope的注解@Scope(“prototype”)。
代理方式:可以使用scoped-proxy属性指定代理,有三个属性值:no,interface,targetClass。
3.8 Bean管理的常用注解
3.8.1 @Required
适用于bean属性(实例域/成员变量)的setter方法(针对bean中的setter方法);这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值。
3.8.2 @Autowired
- 可以将@Autowired(常用)注解用于传统的setter方法,也可以用于构造器或成员变量
- 默认情况下,如果找不到合适的bean将会导致autowiring失败抛出异常,可以通过设置@Autowired(required=false)来避免(使用时先判断bean的成员变量是否为空,避免空指针异常)
- 每个类只能有一个构造器被标记为required=true;@Autowired的必要属性,建议使用@Required注解
- 可以使用@Autowired注解那些常见的解析依赖性接口,如: BeanFactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,MessageSource
- 可以通过添加注解给需要该类型的集合的字段或方法,以提供ApplicationContext中的所有特定类型的bean(数组和Map的自动注入)
- 可以用于装配key为String的Map(key为所有bean的id,value为所有bean的实例对象)
- 如果希望数组有序,可以让bean实现org.springframework.core.Ordered接口或使用@Order注解(只对数组List有效)
- @Autowired是由Spring BeanPostProcessor处理的,所以不能在自身的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解,这些类型必须通过XML或Spring的@Bean注解加载
3.8.3 @Qualifier
按类型自动装配可能多个bean实例的情况,可以使用Spring的@Qualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数;可用于注解集合类型的变量(@Qualifier(“main”))。
<!--在XML配置文件中实现qualifier -->
<bean>
<qualifier value="main/action"/>
</bean>
- 如果通过名字进行注解注入,主要使用的不是@Autowired(即使在技术上能够通过@Qualifier指定bean的名字),替代方式是使用JSR-250@Resource注解,它是通过其独特的名称来定义识别特定的目标(这是一个与所声明的类型无关的匹配过程)
- 因语义差异,集合或Map类型的bean无法通过@Autowired来注入,因为没有类型匹配到这样的Bean,为这些bean使用@Resource注解,通过唯一名称引用集合或Map的bean
- 自定义Qualifier注解(使用@Qualifier注解自定义qualifier注解)
@Autowired适用于fields,Constructors,multi-argument methods这些允许在参数级别使用@Qualifier注解缩小范围的情况。
@Resource适用于成员变量,只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式是使用Qualifier。
3.8.4 @Bean
- @Bean标识一个用于配置和初始化一个由Spring IoC容器管理的新对象(Bean)的方法,类似于XML配置文件
- 可以在Spring的@Component注解的类中使用@Bean注解任何方法(仅仅可以);通常更多的是使用@Configuration结合@Bean
@Bean:
xml | 注解 |
---|---|
自定义Bean Name | @Bean(name=“myFoo”) ; |
init-method | @Bean(initMethod=“init”); |
destory-method | @Bean(destroyMethod=“cleanup”) |
// @ImportResource和@Value,连接数据库配置文件
@ImportResource("classpath:config.xml")
@Value("${url}") @Value("${jdbc.username}")
// @Bean和@Scope,默认@Bean是单例的
@Bean
@Scope("prototype")
@Scope(value="session",proxyMode="ScopedProxyMode.TARGET_CLASS")
// 基于泛型的自动装配(Spring4之后),SPI(针对服务框架开发,基础工具的接口)
@Autowired
private List<Store<Integer>> s;
3.8.5 自定义qualifier注解
CustomAutowiredConfigurer是BeanFactoryPostProcessor的子类,通过它可以注册自己的qualifier注解类型(即使没有使用Spring的@Qualifier注解)。
<!--
该AutowireCandidateResolver决定自动装配的候选者:
1.每个bean定义的autowire-candidate值;
2.任何<bean/>中的default-autowire-candidates;
3.@Qualifier注解及使用CustomAutowireConfigurer的自定义类型。
-->
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="CustomQualifierType">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
3.8.6 @Resource
Spring还支持使用JSR-250@Resource注解的变量或setter方法,这是一种在JavaEE5和6之后的通用模式,Spring管理的对象也支持这种模式。
- @Resource有一个name属性,并且默认Spring解释该值作为被注入bean的名称;
- 如果没有显式地指定@Resource的name属性,则默认的名称是从属性名或者setter方法得出;
- @Resource注解提供的名字被解析为一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的;
- CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解@Resource,在Spring2.5之后引入支持初始化回调(@PostConstruct)和销毁回调(@PreDestroy),前提是CommonAnnotationBeanPostProcessor是在Spring的ApplicationContext中注册的。
Spring3.0之后支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致,需要依赖javax.inject包。
- @Inject等效于@Autowired,可以使用于类,属性,方法,构造器。
- 如果想使用特定名称进行依赖注入,使用@Named,@Named与@Component是等效的。
4. AOP(Aspect Oriented Programming)
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
- 主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等(切面与业务功能垂直)
- 预编译:AspectJ
- 运行期动态代理(JDK动态代理,CGLib动态代理):SpringAOP,JBossAOP
4.1 AOP基本概念
- 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象
- 连接点(Joinpoint):程序执行过程中的某个特定的点
- 通知(Advice):在切面的某个特定的连接点上执行的动作
- 切入点(Pointcut):匹配连接点的断言,在AOP中,通知和一个切入点表达式相关联
- 引入(Introduction):在不修改类代码的前提下,为类添加新的方法和属性
- 目标对象(Target Object):被一个或者多个切面所通知的对象
- AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)
- 织入(Weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入,类加载时织入,执行时织入
Advice类型:
- 前置通知(Before advice):在某连接点(Join point)之前执行的通知,但不能阻止连接点前的执行(除非抛出一个异常)
- 返回后通知(After returning advice):在某连接点正常完成后执行的通知
- 抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知
- 后通知(After(finally) advice):当某连接点退出时执行的通知(不论是正常返回还是异常退出)
- 环绕通知(Around advice):包围一个连接点的通知
4.2 Spring中的AOP
提供了声明式的企业服务,特别是EJB的替代服务的声明;允许用户定制自己的切面,以完成OOP与AOP的互补使用
- Spring的AOP实现:
- 纯java实现,无需特殊的编译过程,不需要控制类加载器层次
- 目前只支持方法执行连接点(通知Spring Bean的方法执行)
- 不是为了提供最完整的AOP实现(功能强大),而是侧重于提供一种AOP实现和Spring IOC容器之间的整合,用于帮助企业应用中常见的问题
Spring AOP不会和AspectJ(完整的AOP实现)竞争,故能提供综合全面的AOP解决方案
有接口和无接口的Spring AOP实现区别:
- Spring AOP默认使用标准的JavaEE动态代理作为AOP代理这使得任何接口(或接口集)都可以被代理
- Spring AOP中也可使用CGLIB代理(如果一个业务对象并没有实现一个接口)
<aop:aspect id="myAspect" ref="myBean">
</aop:aspect>
4.3 Spring中AOP的使用
4.3.1 pointcut
expression="execution()"
// (Spring AOP和AspectJ都支持)
execution(public * *(..)) //切入点为执行所有public方法时
execution(* set*(..)) //切入点为执行所有set开始的方法时
execution(* com.xyz.service.AccountService.*(..)) //切入点为执行AccountService类中的所有方法时
execution(* com.xyz.service..(..)) //切入点为执行com.xyz.service包下的所有方法时
execution(* com.xyz.service...(..)) //切入点为执行com.xyz.service包及其子包下的所有方法时
// (只有Spring AOP支持)
within(com.xyz.service.*)
within(com.xyz.service..*) // within用于匹配指定类型内的方法执行
this(com.xyz.service.AccountService) //this用于匹配当前AOP代理对象类型的执行方法
target(com.xyz.service.AccountService) //target用于匹配当前目标对象类型的执行方法
args(java.io.Serializable) //args用于匹配当前执行的方法传入的参数为指定类型的执行方法
@target(org.springframework.transaction.annotation.Transactional)
@within(org.springframework.transaction.annotation.Transactional)
@annotation(org.springframework.transaction.annotation.Transactional)
@args(com.xyz.security.Classified)
bean(tradeService)
bean(*Service)
4.3.2 advice
<!--Before advice-->
<aop:aspect id="" ref="myBean">
<aop:before pointcut-ref="" method="myBean中的方法"/>
</aop:aspect>
<aop:aspect id="" ref="myBean">
<aop:before pointcut="切入点" method="myBean中的方法"/>
</aop:aspect>
<!--
Around advice:通知方法method的第一个参数必须是ProceedingJoinPoint类型(有一个proceed方法)
Advice parameters
-->
4.3.3 Introductions
<!--
简介(引入)允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象;
由<aop:aspect>中的<aop:declare-parents>元素声明该元素用于声明所匹配的类型拥有一个新的parent(因此得名)
Aspect instantiation models:schema-defined aspects只支持singleton model
-->
<aop:declare-parents types-matching="" implement-interface="" default-impl="" />
4.3.4 Advisors
- advisor就像一个小的自包含的方面,只有一个advice
- 切面自身通过一个bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入点表达式
- Spring通过配置文件中aop:advisor元素支持advisor,实际使用中,大多数情况下它会和transactional advice配合使用
- 为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序
4.3.5 Spring AOP API
Spring AOP基础,必须了解,基于Spring1.2历史用法,4.0后仍然支持,只是更加简便了。
Pointcut:
- 实现之一:NameMatchMethodPointcut,根据方法名字进行匹配
- 成员变量:mappedNames,匹配的方法名集合
- Before advice:一个简单的通知类型,只是在进入方法之前被调用,不需要MethodInvocation对象,前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值。
Throws advice:
- 如果连接点抛出异常,throws advice在连接点返回后被调用
- 如果throws-advice的方法抛出异常,那么它将覆盖原有异常
- 接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅是一个声明,实现类需要实现类似如下方法:public void afterThrowing([Method,args,target],ThrowableSubclass);
After Returning advice:
- 后置通知必须实现org.springframework.aop.AfterReturningAdvice接口
- 可以访问返回值(但不能进行修改),被调用的方法,方法的参数和目标
- 如果抛出异常,将会抛出拦截器链,替代返回值
- Interception around advice:Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点
Introduction advice:
- Spring把引入(简介)通知作为一种特殊的拦截通知
- 使用API的话,需要IntroductionAdvisor和IntroductionInterceptor
- 仅适用于类,不能和任何切入点一起使用
一个Spring test suite的例子:
- 如果调用lock(),希望所有的setter()抛出LockedException(如使物体不可变,AOP典型例子)
- 需要一个完成繁重任务的IntroductionInterceptor(引入拦截器),这种情况下,可以使用org.springframework.aop.support.DelegatingIntroductionInterceptor
- Introduction advisor比较简单,持有独立的LockMixin实例。
Advisor API in Spring:
- Advisor是仅包含一个切入点表达式关联的单个通知的方面
- 除了introductions,advisor可以用于任何通知
- org.springframework.aop.support.DefaultPointcutAdvisor是最常用的advisor类,它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用;它可以混合在Spring同一个AOP代理的advisor和advice中。
ProxyFactoryBean(Spring AOP代理的最基础核心的类):
- 创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean;(实现类似于其他FactoryBean,引入了中间层)
- 通过ProxyFactoryBean这个代理类可以完全控制切入点和通知(advice)以及它们的顺序
- foo(Bean) —> ProxyFactoryBean实现里getObject()创建的对象(getObject()将创建一个AOP代理包装一个目标对象)
- 使用ProxyFactoryBean或者其他IoC相关类来创建AOP代理的最重要好处是通知和切入点也可以由IoC来管理
- 被代理类没有实现任何接口,使用GCLIB代理,否则JDK代理
- 通过设置proxyTargetClass为true,可以强制使用GCLIB
- 如果目标类实现了一个(或多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
- 如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或多个全限定接口名,基于JDK的代理将被创建
- 如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或多个)接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理
- 可以使用匿名内部Bean来隐藏目标和代理之间的区别
Proxying classes:
- 如果没有Person接口,这种情况下Spring会使用CGLIB代理,而不是JDK动态代理
- 即使有接口,也可以强制在任何情况下使用CGLIB
- CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类的委托方法,调用到原来的目标
- 子类是用来实现Decorator模式,织入通知的
- CGLIB的代理对用户是透明的,需要注意:final方法不能被通知,因为他们不能被覆盖,不需要把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心的JAR(基于CGLIB的AOP可以和JDK动态代理一样开箱即用)
- global advisors:用*做通配符,匹配所有拦截器加入通知链
- 简化的proxy定义:使用父子bean定义,以及内部bean定义,可能会带来更简洁的代理定义(抽象属性标记父bean定义为抽象的,这样的话它不能被实例化)
使用ProxyFactory:
/**
使用Spring AOP而不必依赖于Spring IoC:大多数情况下的最佳实践是用IoC容器创建AOP代理;
虽然可以通过硬编码方式实现,但Spring推荐使用配置或注解方式实现。
*/
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface)factory.getProxy();
使用”auto-proxy”:
- Spring允许使用”自动代理”的bean定义,它可以自动代理选定的bean,这是建立在Spring的”bean post processor”功能基础上的(在加载bean的时候就可以修改);
- 主要通过BeanNameAutoProxyCreator来实现
<property name="beanNames" value="jdk*,onlyJdk"/>
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
4.4 AspectJ
- @AspectJ的风格类似纯java注解的普通java类
- Spring可以使用AspectJ来做切入点解析
- AOP的运行时仍旧是纯的Spring AOP,对AspectJ的编译器或者织入无依赖性
Spring中配置@AspectJ:
- 对@AspectJ支持可以使用XML或java风格的配置
确保AspectJ的aspectjweaver.jar库包含在应用程序(版本1.6.8或更高版本)的classpath中
- @Configuration
- @EnableAspectJAutoProxy
- public class AppConfig{}
- aop:aspectj-autoproxy
4.4.1 aspect
- @AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用
- 用@Aspect注解的类可以有方法和字段,它们也可能包括切入点(pointcut),通知(Advice)和引入(introduction)声明
- @Aspect注解是不能够通过类路径自动检测发现的,所以需要配合使用@Component注解或者在xml中配置相应的bean
- 一个类中的@Aspect注解标识它为一个切面,并且将自己从自动代理中排除。
4.4.2 pointcut
- 一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解,方法返回类型必须为void
定义一个名为’anyOldTransfer’,这个切入点将匹配任何名为”transfer”的方法的执行:
@Pointcut(“execution(* transfer(..))”) //the pointcut expression
private void anyOldTransfer(){}//the pointcut signature
Supported Pointcut Designators:
- execution,within,this,target,args,@target,@args,@within,@annotation
组合pointcut:
- 切入点表达式可以通过&&,||和!进行组合,也可以通过名字引用切入点表达式
- 通过组合,可以建立更加复杂的切入点表达式(也不要用太复杂的切入点表达式)
定义良好的pointcut:
- AspectJ是编译期的AOP
- 检查代码并匹配连接点与切入点的代价是昂贵的
一个好的切入点应该包括以下几点:
- 选择特定类型的连接点,如:execution,get,set,call,handler
- 确定连接点范围,如:within,withincode
- 匹配上下文信息,如:this,target,@annotation
4.4.3 Before advice
- After returning advice:有时候需要在通知体内得到返回的实际值,可以使用@AfterReturning绑定返回值的形式。
- After(finally) advice:最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源。
Around advice:
- 环绕通知使用@Around注解来声明,通知方法的第一个参数必须是ProceedingJoinPoint类型
- 在通知内部调用ProceedingJoinPoint的proceed()会导致执行真正的方法,传入一个Object[]对象,数组中的值将被作为参数传递给方法
给advice传递参数:
- Advice的参数以及泛型:Spring AOP可以处理泛型类的声明和使用方法的参数
- Advice参数名称:通知和切入点注解有一个额外的”argNames”属性,它可以用来指定所注解的方法的参数名;如果第一参数是JoinPoint,ProceedingJoinPoint,JoinPoint.StaticPart则可以忽略它
4.4.4 Introductions
允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类来代表这些对象
introduction使用@DeclareParents进行注解,这个注解用来定义匹配的类型拥有一个新的parent:
- 例如,给定一个接口UsageTracked,并且该接口拥有DefaultUsageTracked的实现,接下来的切面声明了所有的service接口的实现,它们都实现了UsageTracked接口。
4.4.5 切面实例化模型
高级主题:
- “perthis”切面通过指定@Aspect注解perthis子句实现
- 每个独立的service对象执行时都会创建一个切面实例
- service对象的每个方法在第一次执行的时候创建切面实例,切面在service对象失效的同时失效。
Restful服务最重要的特性:通过使用超链接,可以进行服务的发现
Springboot:convention over configuration
还没有评论,来说两句吧...