Spring-SpringAOP的实现

对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
	@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());
	}
}

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
{
    @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

LoadTimeWeaver

参考文章:https://www.cnblogs.com/davidwang456/p/5633609.html

你可能感兴趣的:(spring,java)