对Spring AOP的理解
OOP表示面向对象编程,是一种编程思想,AOP表示面向切面编程,也是一种编程思想
Spring AOP:Spring为了让程序员更加方便的做到面向切面编程所提供的技术支持
Spring提供的一套机制,让我们更容易的进行AOP,这套机制就是Spring AOP
扩展:用注解的方式来定义Pointcut和Advice,Spring并不是首创,首创是 AspectJ。JBoss 4.0、aspectwerkz 等技术也提供了对于AOP的支持。
Spring是依赖了AspectJ的,Spring觉得AspectJ中的@Before、@Around等注解比较好用,所以把这些注解直接拿过来用,但是注解底层的解析是由Spring自己做的。
所以我们在用 Spring时,如果你想用@Before、@Around等注解,是需要单独引入aspecj相关jar包的:
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.9.5'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.5'
注意:AspectJ(它自己也是一个项目)是在编译时对字节码进行了修改,可以理解为是在编译时就会去解析@Before这些注解,然后得到代理逻辑,加入到被代理类的字节码中去,所以如果想用AspectJ技术来生成代理对象 ,是需要用单独的AspectJ编译器的。项目中很少用AspectJ编译器,只是用了@Before这些注解,在启动Spring的过程中会去解析这些注解,然后利用动态代理机制生成代理对象。
AOP中的概念
Spring官网:
Let us begin by defining some central AOP concepts and terminology. These terms are not Spring-specific. Unfortunately, AOP terminology is not particularly intuitive. However, it would be even more confusing if Spring used its own terminology
翻译:AOP中的这些概念不是Spring特有的,不幸的是,AOP中的概念不是特别直观的,但是,如果Spring重新定义自己的那可能会导致更加混乱
1、Aspect:表示切面,比如被@Aspect注解的类就是切面,可以在切面中去定义Pointcut、Advice等等
2、Join point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行,比如一个异常的处理,在Spring AOP中,一个连接点通常表示一个方法的执行
3、Advice:表示通知,表示在一个特定连接点上所采取的动作。很多AOP框架中,包括Spring,会用Interceptor拦截器来实现Advice,并且在连接点周围维护一个Interceptor链
4、Pointcut:表示切点,用来匹配一个或多个连接点,Advice与切点表达式是关联在一起的,Advice将会执行在和切点表达式所匹配的连接点上
5、Introduction:可以使用@DeclareParents来给所匹配的类添加一个接口,并指定一个默认实现
6、Target object:目标对象,被代理对象
7、AOP proxy:表示代理工厂,用来创建代理对象的,在Spring Framework中,要么是JDK动态代理,要么是CGLIB代理
8、Weaving:表示织入,表示创建代理对象的动作,这个动作可以发生在编译时期(比如Aspejctj),或者运行时,比如Spring AOP
Advice在Spring AOP中对应的API
Aspject中用五个注解来定义Advice,表示代理逻辑,以及执行时机;Spring有提供类似执行时机的实现类:
Aspject注解(代理逻辑、执行时机) | Spring实现类(类似执行时机) | Spring解析注解为对应的Advice类 |
---|---|---|
@Before | 接口MethodBeforeAdvice, 继承了接口BeforeAdvice | AspectJMethodBeforeAdvice(实际上是MethodBeforeAdvice) |
@AfterReturning | 接口AfterReturningAdvice | AspectJAfterReturningAdvice(实际上是AfterReturningAdvice) |
@AfterThrowing | 接口ThrowsAdvice | AspectJAfterThrowingAdvice(实际上是MethodInterceptor) |
@After | 接口AfterAdvice | AspectJAfterAdvice(实际上是MethodInterceptor) |
@Around | 接口MethodInterceptor | AspectJAroundAdvice(实际上是MethodInterceptor) |
TargetSource的使用
日常的AOP中,被代理对象就是Bean对象,是由BeanFactory创建出来的。
Spring AOP中提供了TargetSource机制,可以用自定义逻辑来创建被代理对象。
@Lazy注解当加在属性上时,会产生一个代理对象赋值给这个属性:
/*** Complete implementation of the* {@link org.springframework.beans.factory.support.AutowireCandidateResolver} strategy* interface, providing support for qualifier annotations as well as for lazy resolution* driven by the {@link Lazy} annotation in the {@code context.annotation} package.** @author Juergen Hoeller* @since 4.0*/
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {@Override@Nullablepublic Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {// 判断是不是懒注入(@Autowired+@Lazy)// 如果是则会在注入时生成一个代理对象注入给属性,所以懒注入并不代表属性为nullreturn (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);}protected boolean isLazy(DependencyDescriptor descriptor) {for (Annotation ann : descriptor.getAnnotations()) {Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);if (lazy != null && lazy.value()) {return true;}}MethodParameter methodParam = descriptor.getMethodParameter();if (methodParam != null) {Method method = methodParam.getMethod();if (method == null || void.class == method.getReturnType()) {Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);if (lazy != null && lazy.value()) {return true;}}}return false;}protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {BeanFactory beanFactory = getBeanFactory();Assert.state(beanFactory instanceof DefaultListableBeanFactory,"BeanFactory needs to be a DefaultListableBeanFactory");final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;TargetSource ts = new TargetSource() {@Overridepublic Class<?> getTargetClass() {return descriptor.getDependencyType();}@Overridepublic boolean isStatic() {return false;}@Overridepublic Object getTarget() {Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);if (target == null) {Class<?> type = getTargetClass();if (Map.class == type) {return Collections.emptyMap();}else if (List.class == type) {return Collections.emptyList();}else if (Set.class == type || Collection.class == type) {return Collections.emptySet();}throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),"Optional dependency not present for lazy injection point");}if (autowiredBeanNames != null) {for (String autowiredBeanName : autowiredBeanNames) {if (dlbf.containsBean(autowiredBeanName)) {dlbf.registerDependentBean(autowiredBeanName, beanName);}}}return target;}@Overridepublic void releaseTarget(Object target) {}};// ProxyFactory生成代理对象,并使用了TargetSource// 所以代理对象在执行某个方法时,调用TargetSource的getTarget()方法实时得到一个被代理对象ProxyFactory pf = new ProxyFactory();pf.setTargetSource(ts);Class<?> dependencyType = descriptor.getDependencyType();if (dependencyType.isInterface()) {pf.addInterface(dependencyType);}return pf.getProxy(dlbf.getBeanClassLoader());}
}
Introduction(@DeclareParents)简介
Spring官网对Introduction和相关注解@DeclareParents的介绍:
Introductions (known as inter-type declarations in AspectJ) enable an aspect to declare that advised objects implement a given interface, and to provide an implementation of that interface on behalf of those objects. An introduction is made using the @DeclareParents annotation. This annotation is used to declare that matching types have a new parent (hence the name).
Introduction有什么用呢?
可以给一个已有的类引入新的接口,在不修改原类的情况下,做一些扩展行为
比如说生产上正在提供服务,这个时候想要加一个验证功能,就可以通过@DeclareParents注解实现
如何使用@DeclareParents注解?
// 第一步,添加一个接口
package com.gax.aop;
public interface Verifier
{boolean validate(User user);
}// 第二步,给接口添加一个实现类
package com.gax.aop;
public class BasicVerifier implements Verifier
{@Overridepublic boolean validate(User user){if (user.getName().equals("gc") && user.getPass().equals("6174")){return true;}return false;}
}// 第三步,使用@DeclareParents注解关联新增接口和原来的类
@Aspect
@Component
public class MyAspect
{@DeclareParents(value = "com.gax.service.UserService",defaultImpl = com.gax.aop.BasicVerifier.class)public Verifier verifer;
}// 第四步,测试效果
public class Test
{public static void main(String[] args){User user = new User();user.setPass("6174888");user.setName("gc");// 创建一个Spring容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");// 注意,这里Verifier是一个接口Verifier verifier = (Verifier) userService;// 验证通过才能提供服务if(verifier.validate(user)){userService.service();}}
}// AppConfig指定扫描包
@ComponentScan(value = "com.gax")
//@EnableAspectJAutoProxy
@Import(AnnotationAwareAspectJAutoProxyCreator.class)
public class AppConfig
{
}@Data
public class User
{private String name;private String pass;
}
@EnableAspectJAutoProxy
@Import(AnnotationAwareAspectJAutoProxyCreator.class)
某些情况下,上面这两种写法等价。@EnableAspectJAutoProxy注解内部其实就是注册了一个AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator其实就是一个BeanPostProcessor,在Spring启动过程中可以去解析AspectJ的注解
参考文章:https://www.cnblogs.com/powerwu/articles/5170861.html
LoadTimeWeaver
参考文章:https://www.cnblogs.com/davidwang456/p/5633609.html