欢迎来到淼淼之森的个人小站。  交流请加我微信好友: studyjava。  也欢迎关注同名公众号:Java学习之道

Spring注解开发总结 - 查询手册及源码分析 置顶!

  |   评论   |   浏览

注册组件

@Configuration

@Configuration等于一个配置文件,如果某个Java类上标注了这个注解,则表示这个类是一个配置类。

@Bean

将一个Java类装配到Spring的IOC容器中,默认是singleton。id默认是方法名。

@Data @AllConstructorArgs @ToString public class Person(){ private String name; private Integer age; } @Configuration public class MainConfig{ @Bean public Person persion(){ return new Person("laowang",25); } } 测试: public class Test1{ @Test public void test1(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Person person = applicationContext.getBean(Person.class); System.out.println(person); } }

@ComponentScan

在配置类上进行标注,指定Spring的扫描规则。

@Configuration @ComponentScan(value="com.laowang.xxx")//使用某个包路径来指定Spring组件的扫描路径,如果想要装配的Bean不在这个指定的路径下,则不会被扫描。 public class MainConfig{ @Bean public Person persion(){ return new Person("laowang",25); } }

excludeFilters排除

指定按照某些规则来排除组件。

使用TpyeFilter指定过滤规则

@Filter的FilterType

  • FilterType.ANNOTATION 按注解排除
@Configuration @ComponentScan(value="com.laowang.xxx",excludeFilters={ @Filter(type=FilterType.ANNOTATION, classes={Controller.class})//排除Controller注解 }) public class MainConfig{ @Bean public Person persion(){ return new Person("laowang",25); } }
  • FilterType.ASPECTJ 按AspectJ表达式排除
  • FilterType.ASSIGNABLE_TYPE 按类排除
@Configuration @ComponentScan(value="com.laowang.xxx",excludeFilters={ @Filter(type=FilterType.ASSIGNABLE_TYPE, classes={Controller.class})//排除Controller注解 }) public class MainConfig{ @Bean public Person persion(){ return new Person("laowang",25); } }
  • FilterType.CUSTOM 按自定义规则排除:需要创建一个类并实现TypeFilter接口
public class MyTypeFilter implememts TypeFilter{ /** * metadataReader读取当前扫描类的信息 * metadataReaderFactory可以获取他类的任何信息 **/ @Override public boolean match(MetadataReader metadataReader,MetadataReaderFactory metadataReaderFactory){ //获取当前类的注解信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类的信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前类的资源信息(类路径等) Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->" + className);//返回类的全路径名 if(className.contains("er")){//如果含有er,则装配 return true; } return false; } } } @Configuration @ComponentScan(value="com.laowang.xxx",excludeFilters={ @Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class})//使用自定义规则的Filter }) public class MainConfig{ @Bean public Person persion(){ return new Person("laowang",25); } }
  • FilterType.REGEX 按正则排除

includeFilters包含

与excludeFilters相反,指定需要哪些组件要被包含扫描。但是要配合userDefaultFilters=false配合使用,因为默认的扫描规则是扫描所有的,如果不配置,则会使用默认的规则进行扫描。

@Configuration @ComponentScan(value="com.laowang.xxx",includeFilters={ @Filter(type=FilterType.ANNOTATION, classes={Controller.class})},//包含Controller注解 userDefaultFilters=false//禁用默认的扫描规则 ) public class MainConfig{ @Bean public Person persion(){ return new Person("laowang",25); } }

@ComponentScans

在jdk8以后,@ComponentScan是个可重复注解@Repeatable。也可以使用@ComponentScans指定扫描策略

@ComponentScans({ ComponentScan(value="com.laowang.xxx",includeFilters={ @Filter(type=FilterType.ANNOTATION, classes={Controller.class})}, userDefaultFilters=false) })

@Scope

设置组件的作用域,如果是单例,则该Bean在装配的时候,Spring会自动创建并放到IOC容器中。如果是多实例,只会在使用到该Bean的时候才创建然后装配。

  • singleton 单例(默认)
@Scope
  • prototype 多实例
@Scope("prototype")
  • request 在web环境中,可以设置为request
  • session 在web环境中,可以设置为session

@Lazy-bean

针对singleton的实例,容器启动的时候先不创建,在第一次使用的时候创建,以后每次都从容器中去取。

@Conditional

按照条件注册Bean。

@Configuration public class MainConfig{ //如果是windows系统,则注册bill,如果是linux则注册linus @Condition({WindowsCondition.class}) @Bean("bill") public Person person1(){ return new Person("Bill",50); } @Condition({LinuxCondition.class}) @Bean("linus") public Person person2(){ return new Person("linus",48); } } public class LinuxCondition implements Condition{ /** * context:判断条件能使用的上下文 * annotatedTypeMetadata:注释信息 **/ @Override public boolean matches(ConditionContext context,AnnotatedTypeMetadata annotatedTypeMetadata){ //判断是否是Linux系统 //能获取到ioc使用的beanFactory ConfigurableListableBeanFactory factory = context.getBeanFactory(); //获取类加载信息 ClassLoader classLoader = context.getClassLoader(); //获取当前信息环境 Environment environment = context.getEnvironment(); //获取到Bean定义的注册类 BeanDefinitionRegistry registry = context.getRegistry(); String property = environment.getProperty("os.name"); if(property.contains("linux")){ return true; } return false; } } public class WindowsCondition implements Condition{ @Override public boolean matches(ConditionContext context,AnnotatedTypeMetadata annotatedTypeMetadata){ //获取当前信息环境 Environment environment = context.getEnvironment(); String property = environment.getProperty("os.name"); if(property.contains("windows 10")){ return true; } return false; } }

备注:可以在运行的时候加参数:-Dos.name=linux进行测试。

Condition接口

public interface Condition{ boolean matches(CondtionContenxt ctxt,AnnotatedTypeMetadata metadata); }

ConditionContext 接口

public interface ConditionContext{ BeanDefinitionRegistry getRegistry();//返回BeanDefinitionRegistry检查bean定义 ConfigurableListableBeanFactory getBeanFactory();//返回ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性 Environment getEnvironment();//返回Environment检查环境变量是否存在以及它的值是什么 ResourceLoader getResourceLoader();//返回ResourceLoader所加载的资源。 ClassLoader getClassLoader();//返回ClassLoader加载并检查类是否存在。 }

AnnotatedTypeMetadata

能够让我们检查带有@Bean注解的方法上还有什么其他的注解。

@Import

快速给容器导入组件,组件名默认为全类名

public class Color{} @Cofiguration @Import({Color.class,xxx.class}) public class MainConfig{ }

ImportSelector

导入选择器,需要自定义类并实现这个接口。

public calss Green{} public class Blue{} public class MyImportSelector implements ImportSelector{ //返回值就是要导入到组件中的类的全类名 //annotationMetadata:当前标注@Import注解类的所有注解信息 @Override public String[] selectImports(AnnotationMetadata annotationMetadata){ //方法不要返回null return new String[]{"xxx.xxx.Green","xxx.xxx.Blue"}; } } @Cofiguration @Import({Color.class,xxx.class,MyImportSelector.class}) public class MainConfig{ }

ImportBeanDefinitionRegistry

给容器中添加组件。

public class Rainbow{ } public class MyImportBeanDefinitionRegistry implements ImportBeanDefinitionRegistry{ //把所有需要添加到容器中的Bean,可以通过BeanDefinitionRegistry.registerBeanDefinition注册进来。 @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,BeanDefinitionRegistry registry){ boolean definition = registry.containsBeanDefinition("xxx.xxx.Green"); if(definition){ RootBeanDefinition beanDefinition = new RootBeanDefinition(Rainbow.class); registry.registerBeanDefinition("rainbow",beanDefinition); } } } @Cofiguration @Import({Color.class,xxx.class,MyImportSelector.class,MyImportBeanDefinitionRegistry.class}) public class MainConfig{ }

FactoryBean

工厂Bean,使用getObject()方法来注册Bean。

public class ColorFactoryBean implements FactoryBean<Color>{ //返回的对象会添加到容器中 @Override public Color getObject() throws Exception{ return new Color(); } //设置返回对象的类型 @Override public Class<?> getObjectType(){ return Color.class; } //控制Bean是否是单例 @Override public boolean isSingleton(){ return false; } } @Cofiguration public class MainConfig{ @Bean ColorFactoryBean colorFactoryBean(){ return new ColorFactoryBean(); //注意,IOC容器会将ColorFactoryBean和Color同时注册。但是使用applicationContext.getBean("ColorFactoryBean");打印的结果是xxx.xxx.Color。因为这个装配的Bean实际是装配了Color这个类,而不是ColorFactoryBean这个类。 //而如果想要获取到这个ColorFactoryBean类的信息,则在获取Bean的时候加上&符号,即applicationContext.getBean("&ColorFactoryBean");打印的结果才是colorFactoryBean。 } }

生命周期

@Bean指定初始化和销毁方法

Bean的生命周期:创建---初始化---销毁的过程。可以自定义初始化和销毁方法。

  • 初始化:在对象完成,并赋值后,调用初始化方法。
  • 销毁:在容器关闭的时候销毁。
  • 单实例Bean会自动初始化和销毁,但是一旦该Bean被装配为多实例,在实际调用的时候才创建该Bean并且容器关闭的时候不会销毁该Bean,需要我们手动销毁。也就是说,在该Bean被装配为多实例后,容器只会帮忙创建而将不再管理该Bean。

指定init和detroy方法

public class Car { public Car(){ System.out.println("car constructor...."); } public void init(){ System.out.println("car init...."); } public void destory(){ System.out.println("car destroy...."); } } @Configuration public class MainConfig{ @Bean(initMethod="init",destroyMethod="destroy") public Car car(){ return new Car(); } } 注意:如果想要看到Bean销毁的效果,需要手动调用applcationContext.close()方法。

InitializingBean和DisposableBean

@Compoent public class Trunk implements InitializingBean,DisposableBean { public Trunk(){ System.out.println("trunk constructor...."); } @Override public void destory() throws Exception{ System.out.println("trunk destory...."); } @Override public void afterPropertiesSet() throws Exception{ System.out.println("trunk afterPropertiesSet...."); } }

@PostConstruct和@PreDestroy

JSR250的规范。

@Component public class Car { public Car(){ System.out.println("car constructor...."); } @PostConstruct public void init(){ System.out.println("car init...."); } @PreDestroy public void destory(){ System.out.println("car destroy...."); } }

BeanPostProcessor接口

在Bean初始化前后做一些事情,具体需要实现postProcessorBeforeInitialization和postProcessorAfterInitialization方法。

public class MyBeanPostProcessor implements BeanPostProcessor{ /** * 可以返回origin bean 也可以返回wrapper bean **/ @Override public Object postProcessorBeforeInitialization(Object bean,String beanName) throws Exception{ System.out.println("postProcessorBeforeInitialization..."); return bean; } @Override public Object postProcessorAfterInitialization(Object bean,String beanName) throws Exception{ System.out.println("postProcessorAfterInitialization..."); return bean; } }

属性赋值

@Value

  • 可以写基本值
  • 可以写SpEL表达式:#{}
  • 可以写${}取出配置文件的值

@PropertySource

指定配置文件。

@PropertySource(value={"classpath:/xxx.properties"})

自动装配

@Autowired&@Qualifier&@Primary

  • @Autowired默认按类型查找,如果找到多个相同类型的组件,再将属性名作为id查找。可以设置required=false,非必须。
  • @Autowired如果放到构造器上,则传入的对象是从容器中获取。如果只有一个有参的构造函数,@Autowired可以省略。
  • @Qualifier 明确指定要装配的组件id
  • @Primary Spring自动装配的时候默认使用首选的Bean。

@Resource&@Inject

  • JSR 250 @Resource,默认按名称进行装配。
  • JSR 330 @Inject,需要引入javax.inject包,与@Autowired功能一样。

@Profile

Spring提供的可以根据当前环境动态激活和切换一系列组件(Bean)的功能。指定组件在哪个环境下才能被注册到容器。不指定的话,任何环境都能注册。

放在组件上

参数:value表示条件,默认值是default,比如:

@Profile("test") @Bean("testDB") xxx @Profile("dev") @Bean("devDB") xxx @Profile("prod") @Bean("prodDB") xxx
  • 开发环境
  • 测试环境
  • 生产环境

放在类上

如果该注解放在类上,则这个类里面所有的组件都只有这个环境下才能会被注册。

使用

  • 可以在运行时增加参数:-Dspring.profiles.active=test来调用相应的环境。
  • 使用代码的方式
1. 创建一个ApplicationContext对象: AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); 2. 设置需要激活的环境,可以设置多个。 applicationContext.getEnvironment().setActiveProfiles("test","dev"); 3. 注册主配置类 applicationContext.register(MainConfig.class); 4. 启动刷新容器 applicationContext.refresh();

Aware接口

BeanNameAware

组件名解析器

ApplicationContextAware

可注入Spring容器上下文。

EmbeddedValueResolverAware

值解析器

等等,如下图所示:

xxxProcessor

用来处理各种Aware注入的问题,以ApplicationContextAwareProcessor为例,底层源码如下:

class ApplicationContextAwareProcessor implements BeanPostProcessor { private final ConfigurableApplicationContext applicationContext; private final StringValueResolver embeddedValueResolver; public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) { this.applicationContext = applicationContext; this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory()); } //此处的作用是在Bean初始化之前做一些事情。 @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { AccessControlContext acc = null; //判断该Bean是否是实现了xxxAware接口。 if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } //判断是否有权限 if (acc != null) { AccessController.doPrivileged(() -> { this.invokeAwareInterfaces(bean); return null; }, acc); //然后调用设置Aware的方法。 } else { this.invokeAwareInterfaces(bean); } return bean; } //实际实现:判断具体是哪个Aware,最终将相应的Aware赋值给该bean的setXxxAware的方法。 private void invokeAwareInterfaces(Object bean) { if (bean instanceof Aware) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware)bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext); } } } //后置处理做一些事情。 public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } }

AOP

概念

面向切面编程,需要增加功能的时候,不在源代码上进行修改,减少代码的耦合。使用面向切面的方式来对要执行的代码的前后逻辑进行控制。

  • @Before前置通知
  • @After后置通知
  • @AfterReturning返回通知
  • @AfterThrowing异常通知
  • @Around环绕通知:动态代理,手动推进方法执行(joinPoint.procceed())。
    例:
//注意,必须开启这个注解,表示启用注解版的切面功能。 @EnableAspectJAutoProxy @Configuration public class MainConfig{ @Bean public Compute compute(){ return new Compute(); } @Bean public LogAspect logAspect(){ return new logAspect(); } } public class Compute { public int div(int i,int j){ return i/j; } } //告诉Spring当前类是个切面类。 @Aspect public class LogAspect{ //也可以在此处创建一个方法,并且下面注解中的参数就填这个方法名。并且带上@PointCut。 @PointCut("execution()") public void pointCut(){ } @Before(切入表达式||"pointCut()") public void logStart(){ System.out.println("logStart..."); } @After(切入表达式) public void logEnd(){ System.out.println("logEnd..."); } //可以获取返回值 @AfterReturning(value=切入表达式,returning="result") public void logReturn(Object result){ System.out.println("logReturn..."); } //可以获取异常信息 @AfterThrowing(value=切入表达式, throwing="exception") public void logExeption(JoinPoint joinPoint,Exception exception){ //可以通过这个参数获取方法的名称、参数等信息。 //获取方法名 joinPoint.getSignature().getName(); //获取参数。 joinPoint.getArgs(); System.out.println("logExeption..."); } } 注意:JoinPoint一定要写在参数第一个。也就是:JoinPoint joinPoint,Exception exception。而不能写到后面:Exception exception,JoinPoint joinPoint,否则会报错。

切面表达式

Spring借助AspectJ的切点表达式来定义Spring切面。
图片来源于网络

execution表达式

execution (<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) execution (* com.sample.service.impl..*.*(..))
  • execution(): 表达式主体。
  • 第一个*号:表示返回类型,*号表示所有的类型。
  • 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
  • 第二个*号:表示类名,*号表示所有的类。
  • *(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。

通过方法签名定义切点

  匹配所有目标类的public方法。第一个*代表返回类型,第二个*代表方法名,而..代表任意入参的方法:

execution(public * *(..))

  匹配目标类所有以To为后缀的方法。第一个*代表返回类型,而*To代表任意以To为后缀的方法:

execution(* *To(..))

通过类定义切点

  匹配Waiter接口的所有方法。第一个*代表返回任意类型,com.baobaotao.Waiter.*代表Waiter接口中的所有方法:

execution(* com.baobaotao.Waiter.*(..))

  匹配Waiter接口及其所有实现类的方法:

execution(* com.baobaotao.Waiter+.*(..))

通过类包定义切点

  在类名模式串中,".*"表示包下的所有类,而"..*"表示包、子孙包下的所有类。
  匹配com.baobaotao包下所有类的所有方法:

execution(* com.baobaotao.*(..))

  匹配com.baobaotao包、子孙包下所有类的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及com.baobaotao.dao.user包下的所有类的所有方法都匹配。".."出现在类名中时,后面必须跟"*",表示包、子孙包下的所有类:

execution(* com.baobaotao..*(..))

  匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切点:

execution(* com..*.*Dao.find*(..))

通过方法入参定义切点

  切点表达式中方法入参部分比较复杂,可以使用"*"和".."通配符,其中"*"表示任意类型的参数,而".."表示任意类型参数且参数个数不限。
  匹配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。它匹配NaughtyWaiter#joke(String,int)方法。如果方法中的入参类型是Java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int):

execution(* joke(String,int)))

  匹配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(Strings1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,double d2,String s3)则不匹配:

execution(* joke(String,*)))

  匹配目标类中的joke()方法,该方法第 一个入参为String,后面可以有任意个入参且入参类型不限,如joke(String s1)、joke(String s1,String s2)和joke(String s1,double d2,String s3)都匹配:

execution(* joke(String,..)))

  匹配目标类中的joke()方法,方法拥有一个入参,且入参是Object类型或该类的子类。它匹配joke(String s1)和joke(Client c)。如果我们定义的切点是execution(* joke(Object)),则只匹配joke(Object object)而不匹配joke(String cc)或joke(Client c)。

execution(* joke(Object+)))

args()和@args()

  args()函数的入参是类名,@args()函数的入参必须是注解类的类名。虽然args()允许在类名后使用+通配符后缀,但该通配符在此处没有意义:添加和不添加效果都一样。

args()

  该函数接受一个类名,表示目标类方法入参对象按类型匹配于指定类时,切点匹配,如下面的例子:

args(com.baobaotao.Waiter)

  表示运行时入参是Waiter类型的方法,它和execution(**(com.baobaotao.Waiter))区别在于后者是针对类方法的签名而言的,而前者则针对运行时的入参类型而言。如args(com.baobaotao.Waiter)既匹配于addWaiter(Waiterwaiter),也匹配于addNaiveWaiter(NaiveWaiter naiveWaiter),而execution(**(com.baobaotao.Waiter))只匹配addWaiter(Waiterwaiter)方法;实际上,args(com.baobaotao.Waiter)等价于execution(**(com.baobaotao.Waiter+)),当然也等价于args(com.baobaotao.Waiter+)。

@args()

  该函数接受一个注解类的类名,当方法的运行时入参对象标注发指定的注解时,方法匹配切点。

组合方式

execution(xxx) && within()
  • 可以使用&&或and表示且;||或or表示或;!或not表示非

获取通知方法的参数

@Aspect public class TrackCounter { @Pointcut(value = "execution(* com.laowang.aop.TestBean.play(int)) && args(trackNumber)") public void trackPlay(int trackNumber) { } @Before(value = "trackPlay(trackNumber)") public void countTrackBefore(int trackNumber) { System.out.println("------before------"); System.out.println("count :" + trackNumber++); } @After(value = "trackPlay(trackNumber)") public void getCount(int trackNumber) { System.out.println("------After------"); System.out.println("count :" + trackNumber); } } -------------------------------------------------- /** * @author wangchao * @date 2020/10/26 13:58 */ @Configuration @EnableAspectJAutoProxy public class ConfigurationTrack { @Bean public TestBean testBean() { return new TestBean(); } @Bean public TrackCounter trackCounter() { return new TrackCounter(); } } --------------------------------------------------- @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = ConfigurationTrack.class) public class TestMain { @Autowired private TestBean testBean; @Autowired private TrackCounter trackCounter; @Test public void test1() { testBean.play(1); testBean.play(2); testBean.play(3); testBean.play(4); } }

@DeclareParents

以透明的方式为被通知的对象引入额外的接口。

/** * @author wangchao * @date 2020/10/26 14:21 * @desc aop用于扩展原始bean中的方法,可以扩展接口的实现类中的方法。 */ public interface Encorable { void performanceEncore(); } ------------------------------------------------- /** * @author wangchao * @date 2020/10/26 14:23 */ public class DefaultEncorable implements Encorable { @Override public void performanceEncore() { System.out.println("test extract method!"); } } ---------------------------------------------- /** * @author wangchao * @date 2020/10/26 14:25 */ @Aspect public class EncorableIntroducer { @DeclareParents(value = "com.laowang.aop.Encorable+", defaultImpl = DefaultEncorable.class) public static Encorable encorable; }

源码分析

@EnableAspectJAutoProxy

这个注解调用了@Import(AspectJAutoProxyRegistrar.class):AspectJAutoProxyRegistrar可以给容器中导入自定义组件。实际上是注册了internalAutoProxyCreator这个组件。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { AspectJAutoProxyRegistrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //如果有需要就注册AspectJAnnotationAutoProxyCreator。 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } } -------------------------> @Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) { //调用注册方法 return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null); } @Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) { //注册或升级AnnotationAwareAspectJAutoProxyCreator return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } --------------------------> @Nullable private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); //判断是否已经有这个组件。 if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) { BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator"); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } else { //否则就从RootBeanDefinition去注册一个AnnotationAwareAspectJAutoProxyCreator RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", -2147483648); beanDefinition.setRole(2); //再注册internalAutoProxyCreator这个组件。 registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition); return beanDefinition; } }

AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator -> AspectJAwareAdvisorAutoProxyCreator -> AbstractAdvisorAutoProxyCreator -> AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor,BeanFactoryAware 后置处理器(在Bean初始化前后做一些事情)
  • AbstractAutoProxyCreator.setBeanFatory()方法。
  • AbstractAutoProxyCreator也有后置处理器的逻辑。
  • AbstractAdvisorAutoProxyCreator重写了setBeanFatory()方法,也就是说实际是调用了子类的setBeanFatory()方法。
  • AbstractAdvisorAutoProxyCreator.setBeanFatory() -> initBeanFactory();
  • AnnotationAwareAspectJAutoProxyCreator重写了initBeanFactory()方法。

分析容器启动加载这些组件的流程

  • 创建IOC容器:调用AnnotationConfigApplicationContext的有参构造函数。
//使用配置类的方式创建Spring容器 public AnnotationConfigApplicationContext(Class... annotatedClasses) { //无参的构造器创建对象 this(); //注册配置类 this.register(annotatedClasses); //刷新容器 this.refresh(); } //使用包扫描路径的方式创建Spring容器。 public AnnotationConfigApplicationContext(String... basePackages) { //无参的构造器创建对象 this(); //扫描包 this.scan(basePackages); //刷新容器 this.refresh(); }
  • refresh()方法:刷新容器,创建所有的Bean及初始化信息。
public void refresh() throws BeansException, IllegalStateException { Object var1 = this.startupShutdownMonitor; synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); this.invokeBeanFactoryPostProcessors(beanFactory); //注册Bean的后置处理器,拦截Bean的创建。 this.registerBeanPostProcessors(beanFactory); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
  • registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)方法,
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this); }
- 先获取IOC容器中已经定义了的需要创建对象的所有BeanPostProcessor。 - 给容器中添加其他的BeanPostProcessor。 - 判断是否是实现了PriorityOrdered的类,如果是,就表示该类有优先加载的权限。 - 再注册是否实现了Ordered接口的类。 - 最后注册普通优先级的类。
  • 从IOC容器中尝试获取BeanPostProcessor,如果没有,则创建。
  • populateBean:给Bean的各种属性赋值。
  • initializeBean:初始化Bean。
    • invokeAwareMethods:处理Aware接口的方法回调。
    • applyBeanPostProcessorBeforeInitialization():应用后置处理器在初始化之前的操作。
    • invokeInitMethods():执行初始化方法。
    • applyBeanPostProcessorAfterInitialization()::应用后置处理器在初始化之后的操作。
  • BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功
aspectJAdvisorsBuilder
  • 把BeanPostProcessor注册到BeanFactory中
beanFactory.addBeanPostProcessor(postProcessor);
  • finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作
  • 创建剩下的单实例bean。
    • 遍历获取容器中所有的Bean:依次创建对象getBean(beanName)(getBean->doGetBean()->getSingleton())

    • 创建bean:AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()。

    • 先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来。

    • createBean();创建bean;AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例

      • BeanPostProcessor是在Bean对象创建完成初始化前后调用的
      • InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
    • resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation
      希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续

      • 后置处理器先尝试返回对象; bean = applyBeanPostProcessorsBeforeInstantiation():拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;就执行postProcessBeforeInstantiation。
      if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); }
  • doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例。

AnnotationAwareAspectJAutoProxyCreator的作用

  1. 每一个bean创建之前,调用postProcessBeforeInstantiation();

    1. 关心MathCalculator和LogAspect的创建
    2. 判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
    3. 判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
    4. 是否需要跳过。
    5. 获取候选的增强器(切面里面的通知方法)【List candidateAdvisors】每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true。
    6. 永远返回false
  2. 创建对象postProcessAfterInitialization;
    return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下

    1. 获取当前bean的所有增强器(通知方法) Object[] specificInterceptors
    2. 找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
    3. 获取到能在bean使用的增强器。
    4. 给增强器排序
    5. 保存当前bean在advisedBeans中;
    6. 如果当前bean需要增强,创建当前bean的代理对象;
    7. 获取所有增强器(通知方法)
    8. 保存到proxyFactory
    9. 创建代理对象:Spring自动决定
      1. JdkDynamicAopProxy(config);jdk动态代理;
      2. ObjenesisCglibAopProxy(config);cglib的动态代理;
    10. 给容器中返回当前组件使用cglib增强了的代理对象;
    11. 以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
  3. 目标方法执行:容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存详细信息(比如增强器,目标对象,xxx)。

    1. CglibAopProxy.intercept();拦截目标方法的执行

    2. 根据ProxyFactory对象获取将要执行的目标方法拦截器链;
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    3. List interceptorList保存所有拦截器(5个,一个默认的ExposeInvocationInterceptor 和 4个增强器)

    4. 遍历所有的增强器,将其转为Interceptor(registry.getInterceptors(advisor))。

    5. 将增强器转为List

      • 如果是MethodInterceptor,直接加入到集合中
      • 如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
        转换完成返回MethodInterceptor数组;
    6. 如果没有拦截器链,直接执行目标方法:拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

    7. 如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个 CglibMethodInvocation 对象,并调用 Object retVal = mi.proceed();

    8. 拦截器链的触发过程;

      • 如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法。
      • 链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行。
      • 拦截器链的机制,保证通知方法与目标方法的执行顺序。
    9. 总结

        1. @EnableAspectJAutoProxy 开启AOP功能
        1. @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
        1. AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
        1. 容器的创建流程:
          1. registerBeanPostProcessors()注册后置处理器;创建AnnotatiAwareAspectJAutoProxyCreator对象
          1. finishBeanFactoryInitialization()初始化剩下的单实例bean
      1. 创建业务逻辑组件和切面组件 2. AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程 3. 组件创建完之后,判断组件是否需要增强 - 是:切面的通知方法,包装成增强器(Advisor)。 - 否:给业务逻辑组件创建一个代理对象(cglib)。 4. 执行目标方法: 1. 代理对象执行目标方法 2. CglibAopProxy.intercept(); - 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor) - 利用拦截器的链式机制,依次进入每一个拦截器进行执行; - 效果: - 正常执行:前置通知-》目标方法-》后置通知-》返回通知 - 出现异常:前置通知-》目标方法-》后置通知-》异常通知```

      Servlet 3.0

      简介

      Servlet3.0提供了一些可插拔的注解,可以不适用配置文件的方式来玩转Servlet,Tomcat7以上版本才支持Servlet3.0。

      @WebServlet

      使用@WebServlet注解创建一个servlet并将该Servlet放到ServletContext容器中。

      @WebServlet(value="/hello") public class HelloServlet extends HttpServlet{ @Override proteced void doGet(HttServletRequest req,HttpServletResponse res){ res.getWriter().write("hello...."); } }

      @WebFilter

      类似@WebServlet,@WebFilter可以往ServletContext容器中注册一个Filter组件,继承Filter。

      @WebFilter(“/foo”) public class MyFilter implements Filter { public void doFilter(HttpServletRequest req, HttpServletResponse res) { ... } }

      @WebListener

      @WebListener public class MyListener implements ServletContextListener{ public void contextInitialized(ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); sc.addServlet("myServlet", "Sample servlet", "foo.bar.MyServlet", null, -1); sc.addServletMapping("myServlet", new String[] { "/urlpattern/*" }); } }

      过滤规则

      • / 表示过滤所有请求,但是不会过滤*.jsp,因为*.jsp文件是交给tomcat的jsp引擎解析。
      • /* 表示过滤所有请求,包括*.jsp的请求。
      • /** 过滤多层所有请求。

      Servlet3.0-ServletContainerListener

      ServletContainerListener会在Servlet启动的时候会扫描所有jar文件中的META-INF/services/文件夹下的所有ServletContainerInitializer的实现文件,文件名为javax.servlet.ServletContainerInitializer,文件内容为ServletContainerInitializer的实现类的全类名。用来初始化注册自定义的组件(spi机制)。
      例:

      1. 在测试项目中的src目录下创建一个META-INF/services/javax.servlet.ServletContainerInitializer文件,并在里面写上自己的ServletContainerInitializer实现类的全路径。
      //@HandlesTypes()这个注解可以将我们指定的类的子类或子接口注册进来,但是不包括扩这个类本身。 @HandlesTypes(value={}) public class MyServletContainerInitializer implements ServletContainerInitializer{ //Set<Class<?>> arg0:注册我们感兴趣的类的所有子类。 //ServletContext arg1:ServletContext容器,也可以通过这个参数手动注册组件(Servlet,Filter,Listener)。 @Override public void onStartup(Set<Class<?>> arg0,ServletContext arg1){ //注册Servlet组件,UserSerlvet为自定义的组件,需要实现HttpServlet接口。 ServletRegistration.Dynamic servlet = arg1.addServlet("userSerlvet",new UserServlet()); //添加Mapping,即匹配路径。 servlet.addMapping("/user"); //类似,注册Listener arg1.addListener(UserListener.class); //注册Filter,类似。 FilterRegistration.Dynamic filter = arg1.addFilter("userFilter",UserFilter()); filter.addMappingForUrlPatterns(EnumSet.of(DispatherType.REQUEST),true,"/**"); } }

      Servlet3.0与SpringMVC整合分析

      1. web容器在启动的时候,会扫描每个jar包下的META-INF/services/javax.servlet.ServletContainerInitializer
      2. 加载这个文件指定的类SpringServletContainerInitializer
      3. spring的应用一启动会加载感兴趣的WebApplicationInitializer接口的下的所有组件;
      4. 并且为WebApplicationInitializer组件创建对象(组件不是接口,不是抽象类)
        1. AbstractContextLoaderInitializer:创建根容器;createRootApplicationContext();
        2. AbstractDispatcherServletInitializer:
          创建一个web的ioc容器;createServletApplicationContext();
          创建了DispatcherServlet;createDispatcherServlet();
          将创建的DispatcherServlet添加到ServletContext中;
          getServletMappings();
        3. AbstractAnnotationConfigDispatcherServletInitializer:注解方式配置的DispatcherServlet初始化器:
          1. 创建根容器:createRootApplicationContext()
            getRootConfigClasses();传入一个配置类。
          2. 创建web的ioc容器: createServletApplicationContext();
            获取配置类;getServletConfigClasses();

      总结:

      以注解方式来启动SpringMVC,继承AbstractAnnotationConfigDispatcherServletInitializer,实现抽象方法指定DispatcherServlet的配置信息。

      SpringMVC整合

      /** * @author laowang * @Date 2019/3/18 14:48 * @Description */ public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { //配置主容器 @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{RootConfig.class}; } //配置副容器 @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{AppConfig.class}; } //配置拦截规则 @Override protected String[] getServletMappings() { return new String[]{"/"}; } } /** * @author laowang * @Date 2019/3/18 14:58 * @Description */ //SpringMVC只扫描Controller,必须禁用默认的扫描规则。 @ComponentScan(value = "com.laowang.spring.servlet3",includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class) },useDefaultFilters = false) public class AppConfig { } /** * @author laowang * @Date 2019/3/18 14:58 * @Description */ //Spring容器不扫描Controller @ComponentScan(value = "com.laowang.spring.servlet3",excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class}) }) public class RootConfig { } /** * @author laowang * @Date 2019/3/18 15:03 * @Description */ @Controller public class HelloController { @Autowired private HelloService helloService; @ResponseBody @RequestMapping("/hello") public String hello() { return helloService.sayHello("laowang"); } } /** * @author laowang * @Date 2019/3/18 15:04 * @Description */ @Service public class HelloService { public String sayHello(String name) { return "hello:" + name; } }

      SpringMVC定制

      /** * @author laowang * @Date 2019/3/18 14:58 * @Description */ //SpringMVC只扫描Controller,必须禁用默认的扫描规则。 @ComponentScan(value = "com.laowang.spring.servlet3",includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class) },useDefaultFilters = false) //开启WebMVC定制配置 @EnableWebMvc public class AppConfig implements WebMvcConfigurer { //启用异步支持 @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { configurer.registerCallableInterceptors(); } //过滤静态资源 @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } //指定视图解析器,可以自定义 @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp("/WEB-INF/views/","jsp"); } //配置拦截规则 @Override public void configurePathMatch(PathMatchConfigurer configurer) { } //配置自定义拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**"); } }

      Servlet3.0异步请求

      /** * @author laowang * @Date 2019/3/18 15:32 * @Description */ //asyncSupported支持异步 @WebServlet(value = "/async", asyncSupported = true) public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //支持异步 AsyncContext asyncContext = req.startAsync(); System.out.println(Thread.currentThread() + "main thread start....." + System.currentTimeMillis()); asyncContext.setTimeout(4000); asyncContext.start(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread() + "sub thread start....." + System.currentTimeMillis()); sayHello(); //告诉容器异步操作执行完毕,可以进行线程回收。 asyncContext.complete(); System.out.println(Thread.currentThread() + "sub thread end....." + System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); } } }); //使用asyncContext获取response并写出数据。 asyncContext.getResponse().getWriter().write("async------------->"); System.out.println(Thread.currentThread() + "main thread end....." + System.currentTimeMillis()); } public void sayHello() throws Exception { Thread.sleep(3000); System.out.println("hello~"); } }

      执行效果

      Thread[http-bio-8080-exec-5,5,main]main thread start.....1552894856004 Thread[http-bio-8080-exec-5,5,main]main thread end.....1552894856006 Thread[http-bio-8080-exec-2,5,main]sub thread start.....1552894856006 hello~ Thread[http-bio-8080-exec-2,5,main]sub thread end.....1552894859007

      SpringMVC异步请求

      返回Callbale

      /** * @author laowang * @Date 2019/3/18 15:42 * @Description */ @Controller public class AsyncController { @ResponseBody @RequestMapping("/async1") public Callable<String> async1() { System.out.println(Thread.currentThread() + "main thread start....." + System.currentTimeMillis()); Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { System.out.println(Thread.currentThread() + "sub thread start....." + System.currentTimeMillis()); Thread.sleep(2000); System.out.println(Thread.currentThread() + "sub thread end....." + System.currentTimeMillis()); return "async1"; } }; System.out.println(Thread.currentThread() + "main thread end....." + System.currentTimeMillis()); return callable; } }

      执行效果

      preHandle........... Thread[http-bio-8080-exec-10,5,main]main thread start.....1552895185639 Thread[http-bio-8080-exec-10,5,main]main thread end.....1552895185640 Thread[MvcAsync1,5,main]sub thread start.....1552895185646 Thread[MvcAsync1,5,main]sub thread end.....1552895187648 preHandle........... postHandler...........

      原理

      1. 控制器返回Callable
      2. Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
      3. DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
      4. Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
      5. 根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
      preHandle.../springmvc-annotation/async1 主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700 主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700 =========DispatcherServlet及所有的Filter退出线程============================ ================等待Callable执行========== 副线程开始...Thread[MvcAsync1,5,main]==>1513932494707 副线程开始...Thread[MvcAsync1,5,main]==>1513932496708 ================Callable执行完成========== ================再次收到之前重发过来的请求======== preHandle.../springmvc-annotation/async1 postHandle...(Callable的之前的返回值就是目标方法的返回值) afterCompletion...

      返回DefferedResult

      /** * @author laowang * @Date 2019/3/18 15:51 * @Description */ //中间件,用来保存队列 public class DeferedQueue { private static Queue<DeferredResult<Object>> defferedQueue = new ConcurrentLinkedDeque<DeferredResult<Object>>(); public static void save(DeferredResult<Object> object) { defferedQueue.add(object); } public static DeferredResult<Object> get() { return defferedQueue.poll(); } } @Controller public class DeferedResultController{ @ResponseBody @RequestMapping("/create-order") public DeferredResult<Object> createOrder() { DeferredResult<Object> deferredResult = new DeferredResult<>(); DeferedQueue.save(deferredResult); return deferredResult; } @ResponseBody @RequestMapping("/create") public String create() { String order = UUID.randomUUID().toString(); DeferredResult<Object> deferedResult = DeferedQueue.get(); deferedResult.setResult(order); return order; } }


标题:Spring注解开发总结 - 查询手册及源码分析
作者:mmzsblog
地址:https://www.mmzsblog.cn/articles/2020/11/24/1606182949496.html

如未加特殊说明,文章均为原创,转载必须注明出处。均采用CC BY-SA 4.0 协议

本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。若本站转载文章遗漏了原文链接,请及时告知,我们将做删除处理!文章观点不代表本网站立场,如需处理请联系首页客服。
• 网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
• 公众号转载请联系网站首页的微信号申请白名单!

个人微信公众号 ↓↓↓                 

微信搜一搜 Java 学习之道

Gitalking ...