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的过程中会去解析这些注解,然后利用动态代理机制生成代理对象。
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
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) |
日常的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
@Nullable
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName)
{
// 判断是不是懒注入(@Autowired+@Lazy)
// 如果是则会在注入时生成一个代理对象注入给属性,所以懒注入并不代表属性为null
return (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() {
@Override
public Class> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
Set 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;
}
@Override
public 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());
}
}
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
{
@Override
public 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
参考文章:https://www.cnblogs.com/davidwang456/p/5633609.html