Spring
中的AOP
需要手动开启 在Spring
中,如果我们采用注解的方式进行AOP
,则需要手动开启Spring
的AOP
支持,如下例子:
① 定义Spring
的配置类,主要声明需要扫描的包路径,并且打开AOP
功能
@Configuration
@ComponentScan("com.single")
@EnableAspectJAutoProxy
public class SpringConfig {
}
@EnableAspectJAutoProxy
该注解即为打开AOP
的注解,我们也可以通过该注解选择动态代理的方式,默认情况下,Spring
采用JDK
的动态代理,我们可以点进该注解看下,请重点看下第一个属性的注释:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
* 翻译过来就是:指示是否创建基于子类(CGLIB)的代理,而不是标准的基于Java接口的代理。默认值为{@code false}
* 说白了就是默认是false,采用JDK,如果你想用Cglib,那你就设置为true
*/
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
②自定义aop
注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AspectAnnotation {
}
③编写切面类、切点、以及增强
@Component
@Aspect
public class AspectTest {
@Pointcut("@annotation(com.single.annotation.AspectAnnotation)")
public void pointCut() {}
@Before("pointCut()")
public void before(JoinPoint point) {
System.out.println("前置增强");
}
@After("pointCut()")
public void after() {
System.out.println("后置增强");
}
}
④在需要被增强的方法上添加自定义注解,方法所在bean
一定要交给Spring
管理
@Service
public class TestService {
public TestService() {
System.out.println("TestService is created...");
}
@AspectAnnotation
public void test() {
System.out.println("init......");
}
}
⑤启动容器并调用方法
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
TestService bean = ctx.getBean(TestService.class);
bean.test();
}
}
SpringBoot
中的AOP
默认开启,并采用Cglib
代理方式 在SpringBoot
中,AOP
的使用与上述没有区别,只不过不需要我们手动开启AOP
功能了,主要是因为SpringBoot
的自动装配,如果你读过SpringBoot
的源码,相比你一定会知道在spring-boot-autoconfigure
的META-INF
下有一个spring.factories
文件,它里面指明了很多需要自动装配的配置类的路径,在启动的时候会自动将这些配置类中定义的bean
装配到IOC
中,原理不多说了,感兴趣的可以去研究一下。
我们来看一下这个文件
可以看到自动配置中有一个AOP
的配置类,找到它,源码如下:
可以看到第53行和61行都用了@EnableAspectJAutoProxy注解
,但是这个配置生效的前提条件是由@ConditionalOnProperty
注解来控制的,通过@ConditionalOnProperty
控制配置类是否生效,可以将配置与代码进行分离,实现了更好的控制配置。例如上图54行:
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class",
havingValue = "false", matchIfMissing = false)
然后再看下META-INF
下的配置文件spring-configuration-metadata.json
{
"name": "spring.aop.proxy-target-class",
"type": "java.lang.Boolean",
"description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).",
"defaultValue": true
},
配置文件代码为defaultValue=true
。@ConditionalOnProperty
实现是通过 havingValue
与配置文件中的值对比,返回为true
则配置类生效,反之失效。由此可知,SpringBoot
的AOP
默认采用的是Cglib
代理方式。其实此处还是涉及到了SpringBoot
自动装配的原理了,而且还是按需加载的,感兴趣的朋友可以研究一下
拓展:@ConditionalOnXXX
,该注解实际上是类加载的一种先决条件,加载当前Bean
必须先存在给定条件,还有 @ConditionalOnMissingXXX
,即加载当前Bean
必须先不存在给定条件,例如
@ConditionalOnNean
:当给定的bean
存在时,则实例化当前Bean
@ConditionalOnMissingBean
:当给定的在bean
不存在时,则实例化当前Bean
,一般用户可覆盖的配置,例如很久之前的一篇关于RedisTemplate
的帖子中提到的SpringBoot
自动装配RedisTemplate
,就用到了这个注解,当你自定义了RedisTemplate
时,其本身的RedisTemplate
就不会被注入,保证只有一个被注入@ConditionalOnWebApplication
:当Spring为web
服务时,才使注解的类生效;通常是配置类;@ConditionalOnResource
:当指定的资源在类路径上时才会生效,例如@ConditionalOnResource(resources="classpath:jdbc.properties")