spring在filter里面怎么获取注解方式定义的bean

spring在filter里面怎么获取注解方式定义的bean,第1张

WebApplicationContext wac = WebApplicationContextUtilsgetRequiredWebApplicationContext(getServletContext()); 有WebApplicationContext 了对象了 spring托管的所有对象都可以拿到了。 当然不推荐这种方式,一般是注入的方式,特殊情况下

1、从功能使用上,没任何本质的区别。

2、两者从spring的实现方式上,则有很大的区别:

注解方式实例化bean,是通过底层框架反射的方式实现,不需要在配置文件中声明任何的bean,只需开启扫描注解功能即可。注解方式耦合度较高,而且反射也会影响效率,不过底层框架已经优化得很好,优点就是编码相对少而简洁;

传统配置的方式则是要在spring的配置文件中按name或id属性配置化实体bean,优点是这个方式耦合度相对较低,相对较直观,缺点就是编码较多,每声明一个实例都要代码获取一遍,且配置文件也要配置,这样会由于过多的bean配置而导致配置文件变得臃肿,维护也不易。

有问题欢迎提问,,谢谢!

一、组件注解

1、 @Component(“xxx”)

指定某个类是容器的bean, @Component(value="xx") 相当于 ,其中 value 可以不写。

用于标注类为spring容器bean的注解有四个, 主要用于区别不同的组件类,提高代码的可读性:

a、 @Component, 用于标注一个普通的bean

b、 @Controller 用于标注一个控制器类(控制层 controller)

c、 @Service 用于标注业务逻辑类(业务逻辑层 service)

d、 @Repository 用于标注DAO数据访问类 (数据访问层 dao)

对于上面四种注解的解析可能是相同的,尽量使用不同的注解提高代码可读性。

注解用于修饰类,当不写value属性值时,默认值为类名首字母小写。

2、 @Scope(“prototype”)

该注解和 @Component 这一类注解联合使用,用于标记该类的作用域,默认 singleton 。

也可以和 @Bean 一起使用,此时 @Scope 修饰一个方法。关于@Bean稍后有说明

3、 @Lazy(true)

指定bean是否延时初始化,相当于 ,默认false。@Lazy可以和@Component这一类注解联合使用修饰类,也可以和@Bean一起使用修饰方法

注 :此处初始化不是指不执行 init-method ,而是不创建bean实例和依赖注入。只有当该bean(被@Lazy修饰的类或方法)被其他bean引用(可以是自动注入的方式)或者执行getBean方法获取,才会真正的创建该bean实例,其实这也是BeanFactory的执行方式。

4、 @DepondsOn({“aa”,“bb”})

该注解也是配合 @Component 这类注解使用,用于强制初始化其他bean

上面的代码指定,初始化bean “userAction"之前需要先初始化“aa”和“bb”两个bean,但是使用了@Lazy(true)所以spring容器初始化时不会初始化"userAction” bean。

5、 @PostConstructor和@PreDestroy

@PostConstructor 和 @PreDestroy 这两个注解是j2ee规范下的注解。这两个注解用于修饰方法,spring用这两个注解管理容器中spring生命周期行为。

a、 @PostConstructor 从名字可以看出构造器之后调用,相当于 。就是在依赖注入之后执行

b、 @PreDestroy 容器销毁之前bean调用的方法,相当于

6、 @Resource(name=“xx”)

@Resource 可以修饰成员变量也可以修饰set方法。当修饰成员变量时可以不写set方法,此时spring会直接使用j2ee规范的Field注入。

@Resource有两个比较重要的属性,name和type

a、 如果指定了name和type,则从Spring容器中找到唯一匹配的bean进行装配,找不到则抛出异常;

b、 如果指定了name,则从spring容器查找名称(id)匹配的bean进行装配,找不到则抛出异常;

c、 如果指定了type,则从spring容器中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常;

d、 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配

如果没有写name属性值时

a、 修饰成员变量,此时name为成员变量名称

b、 修饰set方法,此时name 为set方法的去掉set后首字母小写得到的字符串

7、 @Autowired(required=false)

@Autowired可以修饰构造器,成员变量,set方法,普通方法。@Autowired默认使用byType方式自动装配。required标记该类型的bean是否是必须的,默认为必须存在(true)。

可以配合 @Qualifier(value="xx") ,实现按beanName注入:

a、 required=true(默认),为true时,从spring容器查找和指定类型匹配的bean,匹配不到或匹配多个则抛出异常

b、 使用 @Qualifier("xx") ,则会从spring容器匹配类型和 id 一致的bean,匹配不到则抛出异常

@Autowired会根据修饰的成员选取不同的类型:

a、 修饰成员变量。该类型为成员变量类型

b、 修饰方法,构造器。注入类型为参数的数据类型,当然可以有多个参数

8、demo

业务逻辑层:

数据访问层:

测试类:

输出结果:

可以看到虽然UserDao 使用@Lazy,但是还是在spring容器初始化的时候还是创建了UserDao实例。原因很简单,因为在UserService中需要注入UserDao,所以在此时创建的UserDao实例也属于延时初始化。

在上面我们还使用了两个接口InitializingBean 和DisposableBean,这两个接口用于管理 singleton 作用域的bean的生命周期,类似init-method和destroy-method。不同之处就是调用的循序不一致:

a、 初始化调用顺序 :@PostConstructor > InitializingBean > init-method 用于指定bean依赖注入后的行为

b、 销毁调用顺序 @PreDestroy > DisposableBean > destroy-method 用于定制bean销毁之前的行为

该注解是AspectJ中的注解,并不是spring提供的,所以还需要导入aspectjweaverjar,aspectjrtjar,除此之外还需要依赖aopalliancejar

依赖包:

UserDaojava

配置文件 applicationContextxml:

测试类:

1、 @Aspect

修饰Java类,指定该类为切面类。当spring容器检测到某个bean被@Aspect修饰时,spring容器不会对该bean做增强处理(bean后处理器增强,代理增强)

2、 @Before

修饰方法,before增强处理。用于对目标方法(切入点表达式表示方法)执行前做增强处理。可以用于权限检查,登陆检查。

常用属性:

value: 指定切入点表达式 或者引用一个切入点

对comexampleaop 包下所有的类的所有方法做 before增强处理:

结果:

如果同一条切入点表达式被使用多次,可以使用更友好的方式。定义一个切入点:

增强方法可以接受一个JoinPoint 类型的参数,用于获取被执行目标方法的一下属性。

结果:

3、 @AfterReturning

修饰方法,afterreturning增强处理。目标方法正常结束后做增强处理。

常用属性:

a、 pointcut/value:指定切入点表达式

b、 returning:指定一个参数名,用于接受目标方法正常结束时返回的值。参数名称需要在增强方法中定义同名的参数。

注意:

a、 如果使用了returning 。那么增强方法中的数据类型必须是返回结果的类型或者父类型,否则不会调用该增强处理。

b、 使用了returning 还可以用来 修改返回结果 。

以上面的例子来说,目标方法返回结果类型应该满足下面的条件

修改返回值:

结果:

可以看到 AfterReturning 修改了返回结果。

4、 @AfterThrowing

修饰方法,afterthrowing增强处理。当目标程序方法抛出 异常或者异常无法捕获时,做增强处理。

常用属性:

a、 pointcut/value :指定切入点表达式

b、 throwing:指定一个形参,在增强方法中定义同名形参,用于访问目标方法抛出的异常

参数类型必须是 Throwable 的子类,同样也会有上面@AfterReturning 参数类型匹配的问题。

5、 @After

修饰方法 ,after增强处理。无论方法是否正常结束,都会调用该增强处理(@After= @AfterReturning+@AfterThrowing)。但是该增强方式无法获取目标方法的返回结果,也获取目标方法抛出的异常。所以一般用于进行释放资源,功能类似于 finally。

常用属性:

a、 value :指定切入点表达式

结果:

从上面的结果来看 After 增加处理 ,因为不能接受返回结果作为参数,所以不能修改返回结果。

6、 @Around

修饰方法, around增强处理。该处理可以目标方法执行之前和执行之后织入增强处理(@Before+@AfterReturning)。

Around增强处理通常需要在线程安全的环境下使用,如果@Before和@AfterReturning可以处理就没必要使用@Around。

常用属性:

a、 value :指定切入点表达式

当定义一个Aound增前处理时,增强方法第一形参需要时ProceedingJoinPoint类型。ProceedingJoinPoint有一个Object proceed()方法,用于执行目标方法。当然也可以为目标方法传递数组参数,来修改目前方法的传入参数。

around小结:

a、 Around增强处理通常需要 在线程安全 的环境下使用

b、 调用 proceed()可以获取返回结果,所以可以修改目标方法的返回值

c、 proceed(Object[] var1) 可以修改入参,修改目标方法的入参

d、 可以进行目标方法执行之前和执行之后织入增强处理

around 和 afterReturning 都可以修改返回结果。不过两者的原理不同:

a、 around:可以任意修改,或者返回不相关的值。这个返回值完全可以自主控制

b、 afterReturning,通过方法参数 ,使用对象引用的方式来修改对象。修改对象引用地址那么修改时无效的

除此之外从输出结果来看,增强处理是有序的:

around 和 afterReturning小结:

a、 只有 around 和 afterReturning 可以获取并修改返回结果。需要注意两种方式修改的区别。

b、 around 需要线程安全

c、 虽然增强处理都需要 切入点表达式,并不是都支持 pointcut 属性,所以最好都是用value 属性指定。当注解只需要value属性时,value可以省略

7、 @Pointcut

修饰方法,定义一个切入点表达式用于被其他增强调用。使用该方式定义切入点方便管理,易复用。

切入点方法定义和测试方法定义类似,具有以下特点:

a、 无返回值 (void)

b、 无参数

c、 方法体为空

d、 方法名就是切入点名称

e、 方法名不能为 execution

切入点表达式

切入点表达式可以通过 && 、 || 、 ! 连接

1)、execution 表达式:

2)、within 表达式:

a、匹配指定类下的所有方法。

b、匹配执行包及其子包下所有类的所有方法。

所以within可以看做execution的简写,不需要指定返回类型、方法名、参数( 最小作用单位是类 )

3)、 @annotation:匹配使用指定注解修饰的目标方法;

匹配使用@CustomMethodAnnotation注解的目标方法。

4)、 @within: 用于匹配使用指定注解修饰的类下的所有方法

within 作用范围是类,@within的作用范围与其一致。不同的是@within 指定的不是类而是注解

匹配使用@ResponseBody 注解的类 下的所有方法。

AOP小结:

1)、 Around增强处理通常需要 在线程安全 的环境下使用

2)、 使用 around 和 afterReturning 可以获取并修改返回结果

3)、 增强处理指定 切入点表达式时,最好使用value 属性

4)、 切入点 名称(方法名)不能为 execution

5)、 AfterReturning 指定了 returning 属性接受目标方法返回结果,注意 参数类型需要和返回结果类型一致(满足 resutType instanceof argsType )

增强方式的顺序:

1、 @Bean(name=“xxx”)

修饰方法,该方法的返回值为spring容器中管理的bean。当然该注解和上面的@Component效果一样,主要用于做区分。

@Bean 通常使用在 @Configuration 修饰的配置类中,该注解功能相当于 元素

常用的属性:

a、 name:bean id 。name可以省略,省略时bean名称为方法名。也可以指定多个名称(逗号隔开)。

b、 autowire: 是否自动注入,默认AutowireNO

c、 initMethod:bean的初始化方法。在依赖注入之后执行

d、 destroyMethod: spring容器关闭时bean调用的方法

当然 @Bean 还可以配合 @Scope 指定bean的作用域

2、 @ConfigurationProperties

用于从属性文件中获取值 applicationproperties 或者 applicationyml 。当然了 如果在配置文件中引入其他配置文件,也可以获取到属性值。

包含的属性:

a、 value | prefix 两者互为别名。指定前缀,默认为""

b、 ignoreUnknownFields:默认为true。是否忽略未知字段,当实体中的字段在配置文件中不存在时,是忽略还是抛出异常

c、 ignoreInvalidFields: 默认false。 是否忽略不合法的字段,此处的不合法是指类型不合适,配置文件中存在改配置但是无法转化为指定的字段类型。

Mybatis属性配置

applicationproperties:

ConfigurationProperties 可以配置前缀,然后会根据实体的变量名拼接前缀,去配置文件中查询配置。

3、 @Configuration

修饰一个Java类,被修饰的类相当于一个xml配置文件。功能类似于 。在springboot中大量使用了该注解,该注解提供了一种使用Java类方式配置bean。

可以发现 @Configuration使用了@Component 注解修饰。

实例

配置Mybatis会话工厂

4、 @Import

功能和 类似,修饰Java类,用于向当前类导入其他配置类。 可以导入多个配置文件,通常用于导入不在包扫描范围内的配置文件。可以被扫描的配置类可以直接访问,没有必要使用@Import 导入。

比如 SpringBoot的启动类指定的包扫描路径为 comexample

数据库的配置文件在 com包下。

在MyBatisConfig 中引入 DataSourceConfig, 就会解析DataSourceConfig。将解析出的Bean交给容器管理

5、 @ImportResource

修饰Java类,用于向类引入xml配置文件。

用于导入包含bean定义的配置文件,功能和 类似。默认情况下可以处理后缀为 groovy 和xml 的配置文件

6、 @Value("${expression}")

修饰成员变量或者 方法、构造器的参数,用于属性值注入(在配置文件中配置的值)。

注意: @Value不能对 static 属性注入。

如果的确需要注入到静态变量,可以通过以下方式间接进行注入:

1)、设置一个私有静态 实例

2)、通过构造函数或者 @PostConstruct 注解为 静态实例 赋值,指向本身(this)

3)、对成员属性注入内容

4)、提供静态方法,使用静态实例获取成员属性

7、@PropertySource(value=“classpath:jdbcproperties”)

该注解用来加载属性文件。

常用属性:

a、 ignoreResourceNotFound: 当资源文件找不到的时候是否会忽略该配置,而不是抛出错误。一般用于可选项

b、 encoding : 资源文件使用什么编码方式

c、 value : 指定属性文件位置。可以配置多个属性文件,不可以使用通配符。

在 PropertySource 中可以指定多个路径,并且会将属性文件中的值加载到 Environment 中。

@ConfigurationProperties 和 @PropertySource

它们的使用有一些差异:

1)、 @PropertySource 使用该注解加载的是 相对独立的属性文件,可以同时加载多个文件 (xxxproperties),而且 不支持自动注入 , 不支持前缀注入

2)、 @ConfigurationProperties 用于加载配置文件(applicationproperties | applicationyml)。该注解功能更强大:

a、 支持前缀注入 ( prefix )

b、 相同属性名的自动注入

c、 $("") 支持EL表达式注入

应用实例:

在以往的开发中通常会将数据库连接信息存放在单独的属性文件中(jdbcproperties)。而在spring boot 中我们会将数据库的信息存放在配置文件中,这会极大便利开发工作。

jdbcproperties:

可以通过 @Value 注解将配置文件的值注入到实体类中

也可以注入Environment ,通过Environment 获取值

1、 @ResponseBody

控制器方法返回值会使用 >

private void beforeTransationHandle(JoinPoint point) throws Exception{

//拦截的实体类

Object target = pointgetTarget();

//拦截的方法名称

String methodName = pointgetSignature()getName();

//拦截的方法参数

Object[] args = pointgetArgs();

//拦截的放参数类型

Class[] parameterTypes = ((MethodSignature)pointgetSignature())getMethod()getParameterTypes();

Method m = null;

try {

//通过反射获得拦截的method

m = targetgetClass()getMethod(methodName, parameterTypes);

//如果是桥则要获得实际拦截的method

if(misBridge()){

for(int i = 0; i < argslength; i++){

//获得泛型类型

Class genClazz = GenericsUtilsgetSuperClassGenricType(targetgetClass());

//根据实际参数类型替换parameterType中的类型

if(args[i]getClass()isAssignableFrom(genClazz)){

parameterTypes[i] = genClazz;

}

}

//获得parameterType参数类型的方法

m = targetgetClass()getMethod(methodName, parameterTypes);

}

} catch (SecurityException e) {

eprintStackTrace();

} catch (NoSuchMethodException e) {

eprintStackTrace();

}

}

1 可以通过ApplicationContext的实例来查找。

2 ApplicationContext 的实例 可以通过实现 一个ApplicationContextAware接口,并将实现ApplicationContextAware接口的类做为一个Bean, Spring在装配过程中,会装配ApplicationContext。

3你的listener启动的顺序,应该晚于spring的listener启动。

众所周知, spring 从 25 版本以后开始支持使用注解代替繁琐的 xml 配置,到了 springboot 更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往在一个注解上总会出现一些其他的注解,比如 @Service :

大部分情况下,我们可以将 @Service 注解等同于 @Component 注解使用,则是因为 spring 基于其 JDK 对 元注解的机制 进行了扩展。

在 java 中,元注解是指可以注解在其他注解上的注解,spring 中通过对这个机制进行了扩展,实现了一些原生 JDK 不支持的功能,比如允许在注解中让两个属性互为别名,或者将一个带有元注解的子注解直接作为元注解看待,或者在这个基础上,通过 @AliasFor 或者同名策略让子注解的值覆盖元注解的值。

本文将基于 spring 源码 52x 分支,解析 spring 如何实现这套功能的。

这是系列的第三篇文章,将详细介绍 Spring 是如何在经过搜索与属性映射后,将处理后的注解合成为合并注解的。

相关文章:

我们在前文了解用于搜索注解的合并注解聚合 MergedAnnotations 与用于完成注解属性映射的 AnnotationTypeMappings 和 AnnotationTypeMapping ,现在我们需要知道在 MergedAnnotations 这个容器中, AnnotationTypeMappings 和 AnnotationTypeMapping 是如何转为一个我们所需要的合并注解 MergedAnnotation 的。

与前文一样,我们以 AnnotatedElementUtilsfindMergedAnnotations 方法作为入口:

我们在上文顺着 MergedAnnotationsget 一路找到 TypeMappedAnnotationsMergedAnnotationFinder 的 process 方法,在这里我们目睹了一个普通的注解的元注解被解析为 AnnotationTypeMappings 与 AnnotationTypeMapping 的过程:

该方法是 AnnotationTypeMapping 转为 MergedAnnotation 的关键。

TypeMappedAnnotation 是 MergedAnnotation 一个通用实现,在大部分情况下,我们所说的合并注解其实指的就是这个类。

通过它的构造方法我们得以了解其创建过程:

可以看得出, TypeMappedAnnotation 基本可以认为是 AnnotationTypeMapping 的包装类,它以一个 AnnotationTypeMapping 实例作为数据源,从而提供一些关于映射后的属性的相关功能。

回到 AnnotatedElementUtilsfindMergedAnnotations ,我们可以看到,在通过 MergedAnnotations 获得了一个 MergedAnnotation 对象——实际上是 TypeMappedAnnotation 对象——之后,又调用了 MergedAnnotationsynthesize 方法,将 MergedAnnotation 转成了一个调用方指定类型的注解对象。

该方法先调用了 AbstractMergedAnnotation 的 synthesize 方法:

随后再调用了实现类 TypeMappedAnnotation 的 synthesize 方法:

继续点开 createSynthesized :

而 SynthesizedMergedAnnotationInvocationHandler 是一个用于 JDK 动态代理的 InvocationHandler ,我们不需要完全站看,仅需看看它的构造函数与 InvocationHandlerinvoke 就能明白它的运作机制了:

至此,合并注解的合成机制已经很明确了:

承接上文,当我们使用 MergedAnnotationsynthesize 方法时,我们可能会得到两种对象:

而通过注解代理对象取值时,这些方法会被代理到 SynthesizedMergedAnnotationInvocationHandler 中存放的 MergedAnnotation 对象上,从而让这个代理对象通过原始注解的属性,获得与原始注解不一样的属性值。

当我们调用代理对象的属性值时,它会在 SynthesizedMergedAnnotationInvocationHandler 中,通过 invoke 代理到对应的方法上:

这里我们代理对象是如何获取注解属性值的:

这里的 MergedAnnotationgetValue 最终在经过多次跳转后,调到 TypeMappedAnnotationgetAttributeValue 上:

而这边的 getValue 方法就是真正要获取属性值的地方。

这一步有点复杂,主要是根据不同的情况,通过 AnnotationTypeMapping 中的几个属性映射数组,包括 aliasMappings 、 conventionMappings , annotationValueMappings 与 annotationValueSource 来确定最终用于取值的 AnnotationTypeMapping 对象与调用的方法在 AttributeMethods 中的下标:

至此,获取属性值的方法流程也走完了。

在这一章,我们了解了当通过 MergedAnnotations 获得注解并解析得到 AnnotationTypeMapping 后, AnnotationTypeMapping 是如何再转为我们所需的 MergedAnnotation ,以及在此之后, MergedAnnotation 又是如何生成我们最终所需要的代理注解的。

简而言之,当解析注解的元注解获得所需的 AnnotationTypeMapping 后, MergedAnnotation 会判断 AnnotationTypeMapping 是否发生过属性映射,如果没有则返回该映射对象对应的原始注解,否则就通过 SynthesizedMergedAnnotationInvocationHandler 生成一个对应类型的 JDK 动态代理对象。

当我们通过代理对象去调用注解的方法,获取注解的属性的时候, SynthesizedMergedAnnotationInvocationHandler 会把方法代理到对应的内部方法中,而获取属性时,还会通过 MergedAnnotationgetValue ,最终绕到 AnnotationTypeMapping 中获取被映射后的属性值。

《spring常用的注解》百度网盘资源下载 ida4   

注解本身没有功能的,就和 xml 一样。注解和 xml 都是一种元数据,元数据即解释数据的数据,这就是所谓配置。本文主要罗列 Spring|Spring MVC相关注解的简介。

以上就是关于spring在filter里面怎么获取注解方式定义的bean全部的内容,包括:spring在filter里面怎么获取注解方式定义的bean、一个采用注解方式,一个采用从spring框架中获取方式,这两种方法有何区别吗、spring常用注解等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9795865.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-02
下一篇 2023-05-02

发表评论

登录后才能评论

评论列表(0条)

保存