写点什么

Spring 注解驱动,java 面试项目中遇到的问题

  • 2021 年 11 月 10 日
  • 本文字数:7511 字

    阅读完需:约 25 分钟

@ComponentScan(basePackages= {“com.bdr._"},excludeFilters = { @Filter(type = FilterType.ANNOTATION, classes = Controller.class) })


@ComponentScan(value = "com.bdr._”, excludeFilters = { @Filter(type = FilterType.CUSTOM, value = MyTypeFilter.class) }, useDefaultFilters = false)


public class MyTypeFilter implements TypeFilter {


/**


  • 自定义匹配模式,使用自定义的的过滤器,需要设置 @Filter type = FilterType.CUSTOM、 属性 useDefaultFilters = false


*/


public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)


throws IOException {


AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();


String[] memberClassNames = annotationMetadata.getMemberClassNames();


for (String v: memberClassNames) {


System.out.println("memberClassNames:"+v);


}


return false;


}


}


1.1.3. @Scope


常用于 bean 作用域于注解,定义 Spring 容器创建实例的方式。


/**


  • prototype:多实例,每次获取 bean 都会从容器中创建一个新的实例

  • singleton:单例,容器启动就创建一个实例

  • request:一次请求创建一个实例

  • session:一个会话创建一个实例

  • @return


*/


@Bean


@Scope("prototype")


public Employee myBean() {


Employee employee = new Employee();


return employee;


}


1.1.4. @Lazy


用于定义容器创建实例的时机(懒加载机制),容器创建时不实例化 bean,当使用到该 bean 时实例化 bean,该注解默认为 value=true


@Bean


@Lazy


@Scope("prototype")


public Employee myBean() {


Employee employee = new Employee();


return employee;


}


1.1.5. @Import


用于快速向 Spring 容器添加 Bean,该注解支持 Configuration、ImportSelector、ImportBeanDefinitionRegistrar 自定义导入方式和导入的类;@Import 导入时的是全类名。


  • ImportSelector


@Import(value = { Depart.class, MyImportSelector.class })


public class MyImportSelector implements ImportSelector{


/**


  • 这里的返回值,不要返回 null,返回一个空数组也可以

  • 返回的数组就是,对应需要导入的包,通过 ImportSelector 自定导入需要的包


*/


public String[] selectImports(AnnotationMetadata importingClassMetadata) {


System.out.println("MyImportSelector>>>>: "+importingClassMetadata.getSuperClassName());


System.out.println("MyImportSelector>>>>: "+importingClassMetadata.getEnclosingClassName());


// 返回指定全类名的包,导入该指定包


return new String[] {"com.bdr.bean.Depart4"};


}


}


  • ImportBeanDefinitionRegistrar


@Import(value = { Depart.class, MyImportBeanDefinitionRegistrar.class })


public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {


/**


  • 通过 ImportBeanDefinitionRegistrar 导入指定的 bean


*/


public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {


// importingClassMetadata 获取注解信息,BeanDefinitionRegistry bean 注入操作。


boolean containsBeanDefinition = registry.containsBeanDefinition("myWindowBean");


if(containsBeanDefinition) {


RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Depart4.class);


registry.registerBeanDefinition("depart4444", rootBeanDefinition);


}


}


}


  • FactoryBean


// MyFactoryBean


public class MyFactoryBean implements FactoryBean<Depart5> {


/**


  • 返回实例


*/


public Depart5 getObject() throws Exception {


return new Depart5();


}


/**


  • 指定类型


*/


public Class<?> getObjectType() {


return Depart5.class;


}


/**


  • 通过 FactoryBean 导入 bean

  • @return


*/


@Bean


public MyFactoryBean myFactoryBean() {


return new MyFactoryBean();


}


@Test


public void getConfig() {


AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);


String[] beans = context.getBeanDefinitionNames();


for (String v : beans) {


System.out.println(v);


}


// 获取 MyFacoryBean


// 通过 myFactoryBean 获取到的是 FactoryBean 创建的实例 bean


// 若需要获取工厂 bean 类型,需要加上前缀 &


Object bean = context.getBean("&myFactoryBean");


System.out.println("bean:::: "+bean);


}


1.1.6. @Bean


向容器中注入 Bean,该注解可以作用于一个方法上、注解上。通过该注解可以指定注入 bean 的注入方式(BY_NAME,BY_TYPE)、bean 名称、bean 的 initMethod 和 destroyMethod,(这个方法可以写在需要注入的 Bean 中)


public class Employee {


private String name;


private String no;


public String getNo() {


return no;


}


public void setNo(String no) {


this.no = no;


}


public Employee() {


System.out.println("我被创建了。。。。。。。。。。");


}


public void init() {


System.out.println("我的 Bean 正在初始化。。。。。");


}


public void destory() {


System.out.println("我的 Bean 正在销毁。。。。。");


}


@Bean(initMethod = "init", destroyMethod = "destory")


@Scope("prototype")


public Employee myBean() {


Employee employee = new Employee();


return employee;


}


@Test


public void getConfigOfLifeCycle() {


AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);


String[] beans = context.getBeanDefinitionNames();


for (String v : beans) {


System.out.println(v);


}


//context.getBean("myBean");


}


1.1.7. @Value/@PropertySource/@ImportResource/@ConfigurationProperties


使用这几个注解需要使用 @Component 注解(标记是 Spring 的组件)


1、 @Value: 可以对 bean 属性赋值,支持 SqEL 表达式:#{}-在里面写 SqEL 表达式,${}-获取属性文件中的值


2、 @PropertySource: 加载指定的配置文件 @PropertySource(value = {“classpath:person.properties”})


3、@ConfigurationProperties: 将本类中的所有属性和配置文件中相关的配置进行绑定;prefix = “person”:配置文件中哪个下面的所有属性进行一一映射;ConfigurationProperties(prefix = “person”)默认从全局配置文件中获取值;


4、@ImportResource: 导入 Spring 的配置文件,让配置文件里面的内容生效;若在 Spring Boot 里想让让 xml 配置文件生效,可以使用 @ImportResource(locations = {“classpath:beans.xml”})方式加载进来,然后让配置生效。


1.1.8. 自动装配


1、 @Autowired: 自动注入 Bean,①优先按照 bean 的类型匹配注入,②若有多个类型的 bean,则根据 bean 的名称去匹配;③该注解可以标在构造器、参数、方法、属性上,并且都是从容器中获取参数组件的值;④标注在方法上,参数会从容器中获取;默认不写 @Autowired 效果是一样的,都能实现自动装配;⑤标在构造器上:如果组件只有一个有参构造器,这个有参构造器的 @Autowired 可以省略,参数位置的组件还是可以自动从容器中获取。


详情可参考:https://docs.spring.io/spring/docs/5.1.5.RELEASE/spring-framework-reference/core.html#beans-annotation-config


//默认加在 ioc 容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作


@Component


public class Boss {


private Car car;


//构造器要用的组件,都是从容器中获取


// 如果组件只有一个有参构造器,这个有参构造器的 @Autowired 可以省略,参数位置的组件还是可以自动从容器中获取


public Boss(Car car){


this.car = car;


System.out.println("Boss...有参构造器");


}


public Car getCar() {


return car;


}


//@Autowired


//标注在方法,Spring 容器创建当前对象,就会调用方法,完成赋值;


//方法使用的参数,自定义类型的值从 ioc 容器中获取


public void setCar(Car car) {


this.car = car;


}


@Override


public String toString() {


return "Boss [car=" + car + "]";


}


}


2、@Qualifer: 通过 bean 名称指定注入 bean,一般配合 @Autowired 使用。


3、@Primary: 指定首选需要注入的 Bean。


4、Spring 还支持使用 @Resource (JSR250)和 @Inject (JSR330)[java 规范的注解]


  • @Resource: 可以和 @Autowired 一样实现自动装配功能;默认是按照组件名称进行装配的; 不支持 @Primary、@Autowired(r


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


eqiured=false);


  • @Inject: 需要导入 javax.inject 包,和 Autowired 的功能一样。没有 required=false 的功能;


注: @Autowired 是 Spring 定义的, @Resource、@Inject 都是 java 规范。


5、Spring Aware


Spring Aware 是属于 Spring 内部调用的方式。若自定义实现 Spring Aware 相关接口,则 bean 会与 spring 耦合在一起(可以别容器管理),可以使用 Spring 底层资源。


| 接口名称 | 说明 | 备注 |


| :-: | :-: | :-: |


| BeanNameAware | BeanNameAware 获取容器中 bean 的名称 | |


| BeanFactoryAware | 获取当前的 BeanFactory,调用容器中的服务 | |


| ApplicationContextAware* | 获取 spring 中的所有服务 | |


| MessageSourceAware | 获取 messages source 可以获取相关的文本信息 | |


| ApplicationEvenPublisherAware | 应用时间发布器,可以发布时间 | |


| ResourceLoaderAware | 获取资源信息,可以获取外部资源 | |


1.1.9. @Profile


spring 提供开发环境、开发部署切换的一个属性


1.1.10. @Conditional


满足一定条件,才向上下文容器中注入 bean,在 Spring 中有许多拓展 @Conditional 的注解,value 为 org.springframework.context.annotation.Condition 的实现类,如:


@Bean


@Conditional(WindowsConditional.class)


public Employee myWindowBean() {


Employee employee = new Employee();


return employee;


}


/**


  • 实现 Condition 接口完成 bean 条件注入

  • @author Rick


*/


public class WindowsConditional implements Condition {


// 返回 true 时注入


public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {


// ConfigurableListableBeanFactory 可以实现注入 bean,获取 bean,对 bean 的操作


ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();


System.out.println("==============WindowsConditional================");


String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();


for (String beans : beanDefinitionNames) {


System.out.println("获取到 beans:" + beans);


}


System.out.println("==============================");


ClassLoader classLoader = context.getClassLoader();


System.out.println("获取到 ClassLoader:" + classLoader.toString());


System.out.println("==============================");


Environment environment = context.getEnvironment();


System.out.println("获取操作系统环境:" + environment.getProperty("os.name"));


System.out.println("==============================");


BeanDefinitionRegistry registry = context.getRegistry();


registry.registerAlias("myBean", "employeeRegistryWindows");


String[] registrybeans = registry.getBeanDefinitionNames();


for (String registrybean : registrybeans) {


System.out.println("获取注入的 bean:" + registrybean);


}


ResourceLoader resourceLoader = context.getResourceLoader();


System.out.println("==============================");


// 获取到注解参数信息


MultiValueMap<String, Object> allAnnotationAttributes = metadata


.getAllAnnotationAttributes("org.springframework.context.annotation.Bean");


for (String key : allAnnotationAttributes.keySet()) {


System.out.println("获取注解:" + allAnnotationAttributes.get(key));


}


// 若在 windows 系统上则注入该 bean


String sysOs = environment.getProperty("os.name");


if (sysOs.contains("Windows")) {


return true;


}


return false;


}


作用: 必须是 @Conditional 指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效。


常见 Conditional 扩展注解


| @Conditional 扩展注解 | 作用(判断是否满足当前指定条件) |


| :-: | :-: |


| @ConditionalOnJava | 系统的 java 版本是否符合要求 |


| @ConditionalOnBean | 容器中存在指定 Bean; |


| @ConditionalOnMissingBean | 容器中不存在指定 Bean; |


| @ConditionalOnExpression | 满足 SpEL 表达式指定 |


| @ConditionalOnClass | 系统中有指定的类 |


| @ConditionalOnMissingClass | 系统中没有指定的类 |


| @ConditionalOnSingleCandidate | 容器中只有一个指定的 Bean,或者这个 Bean 是首选 Bean |


| @ConditionalOnProperty | 系统中指定的属性是否有指定的值 |


| @ConditionalOnResource | 类路径下是否存在指定资源文件 |


| @ConditionalOnWebApplication | 当前是 web 环境 |


| @ConditionalOnNotWebApplication | 当前不是 web 环境 |


| @ConditionalOnJndi | JNDI 存在指定项 |


1.1.11. @RequestMapping


映射 URL 请求到某个方法上或者 Controller 上


属性说明 ?


value: 请求的 URL 的路径,支持 U 也模板、正则表达式,支持 Ant 风格。


method: HTTP 请求方法,有 GET、POST、PUT 等。


** consumes:** 允许的媒体类型(MediaTypes),如 consumes=”application/ison”,对应于请求的 HTTP 的 Content-Type。


produces: 相应的媒体类型,如 produces=”application/json”,对应于 HTTP 的 Accept 宇段。


params: 请求的参数,如 params=”action=update”。


headers: 请求的 HTTP 头的值,如 headers=”myHeader=myValue”。


// 简化的 @RequestMapping,RESTFul


@GetMapping;


@PostMapping;


@PutMapping;


@DeleteMapping;


@PatchMapping


1.1.12. @PathVariable


变量占位符,可以将 URL 中的值映射到方法参数中。


// 如果映射名称不一致需要制定 @PathVariable("userId")


@GetMapping(path="/{userId}{type}/get.do")


@ResponseBody


public User getUser(@PathVariable("userId") Long id, @PathVariable Integer type) {


}


1.1.13. @ModeIAttribute


注解 ModelAttribute 通常作用在 Controller 的某个方法上,此方法会首先被调用,井将方法结果作为 Model 的属性,然后再调用对应的 Controller 处理方法。


1.1.14. @RequsetBody


接受 Json 格式请求,意味着请求的 HTTP 消息体的内容是一个 JSON


1.1.15. @Controller/@RestController


定义个 springMVC 控制器,RestController 是 @Controller 和 @ResponseBody 注解的组合。


1.1.16. @ControllerAdvice/@RestControllerAdvice


ControllerAdvice 注解相当于是 Controller 层的全局配置,在标注注解 ControllerAdvice 的配置可以所用所有的 Controller 下的每个请求中,常常用于处理全局异常,在该注解配置中可以包含 @ExceptionHandler、@InitBinder 和 @ModelAttribute。


@RestControllerAdvice 同比 @Controller/@RestController ,由 @ControllerAdvice 和 @ResponseBody 组成。


@ControllerAdvice


public class MyControllerAdvice {


/**


  • 应用到所有 @RequestMapping 注解方法,在其执行之前初始化数据绑定器

  • @param binder


*/


@InitBinder


public void initBinder(WebDataBinder binder) {}


/**


  • 把值绑定到 Model 中,使全局 @RequestMapping 可以获取到该值

  • @param model


*/


@ModelAttribute


public void addAttributes(Model model) {


model.addAttribute("author", "Magical Sam");


}


/**


  • 全局异常捕捉处理

  • @param ex

  • @return


*/


@ResponseBody


@ExceptionHandler(value = Exception.class)


public Map errorHandler(Exception ex) {


Map map = new HashMap();


map.put("code", 100);


map.put("msg", ex.getMessage());


return map;


}


}

1.2. Aop 注解

1.2.1. 简介


| 注解 | 说明 | 备注 |


| :-: | :-: | :-: |


| @Before | 在目标方法执行之前通知 | |


| @After | 在目标方法执行之后通知 | |


| @AfterRturning | 在目标方法执行正常返回后通知 | |


| @AfterThrowing | 在目标方法执行出现异常后通知 | |


| @Around | 环绕通知 | |


| @PointCut | 定义切点 | |


| @EnableAspectJAutoProxy | 开启切面支持 | |


1.2.2. 步骤


1)、将业务逻辑组件和切面类都加入到容器中;告诉 Spring 哪个是切面类(@Aspect)


2)、在切面类上的每一个通知方法上标注通知注解,告诉 Spring 何时何地运行(切入点表达式)


3)、开启基于注解的 aop 模式;@EnableAspectJAutoProxy


1.2.3. 代码


package com.bdr.bean;


import java.util.Arrays;


import org.aspectj.lang.JoinPoint;


import org.aspectj.lang.annotation.After;


import org.aspectj.lang.annotation.AfterReturning;


import org.aspectj.lang.annotation.AfterThrowing;


import org.aspectj.lang.annotation.Aspect;


import org.aspectj.lang.annotation.Before;


import org.aspectj.lang.annotation.Pointcut;


// 定义切面


@Aspect


public class AopDefinition {


// 定义切点,也就是切入到那个目标类、目标方法上,切点名就是方法名


// 切点表达式:作用该类下所有的公共方法,相关切面表达式查看官方文档


@Pointcut("execution(public int com.bdr.bean.AopPointCutTest.*(..))")


public void myPointCut() {


}


// @Before 在目标方法之前切入;切入点表达式(指定在哪个方法切入),上面的切点直接引用即可


// 引入 JoinPoint 用于获取方法执行参数信息


@Before("myPointCut()")


public void logStart(JoinPoint joinPoint) {


Object[] args = joinPoint.getArgs();


System.out.println("" + joinPoint.getSignature().getName() + "运行。。。@Before:参数列表是:{" + Arrays.asList(args) + "}");


}


@After("myPointCut()")


public void logEnd(JoinPoint joinPoint) {


System.out.println("" + joinPoint.getSignature().getName() + "结束。。。@After");


}


// JoinPoint 一定要出现在参数表的第一位


@AfterReturning(value = "myPointCut()", returning = "result")


public void logReturn(JoinPoint joinPoint, Object result) {


System.out.println("" + joinPoint.getSignature().getName() + "正常返回。。。@AfterReturning:运行结果:{" + result + "}");


}


@AfterThrowing(value = "myPointCut()", throwing = "exception")


public void logException(JoinPoint joinPoint, Exception exception) {


System.out.println("" + joinPoint.getSignature().getName() + "异常。。。异常信息:{" + exception + "}");


}


}


package com.bdr.config;


import org.springframework.context.annotation.Bean;


import org.springframework.context.annotation.Configuration;

评论

发布
暂无评论
Spring注解驱动,java面试项目中遇到的问题