关于Spring的那些事儿

目录

Bean

Q1:spring的bean是单例的吗?

Q2:单例bean是线程安全的吗?

Bean的生命周期

Bean的循环依赖

AOP

常见AOP使用场景

1.记录操作日志

2.缓存处理

3.Spring中内置的事务处理

Spring事务失效的场景

1.异常捕获处理

2.抛出检查异常

3.非public方法

SpringMVC

执行流程

视图阶段流程:

前后端分离阶段

Springboot

自动配置原理

常见注解

Spring常见注解

SpringMVC常见注解

Springboot常见注解


在当今的软件开发领域,Spring已成为 Java 开发者们不可或缺的利器之一。它是一个功能强大且广泛应用的应用程序开发框架,以其灵活性、可扩展性和松耦合的特性而备受赞誉。然而,要真正理解 Spring 在背后所做的事情,我们需要深入探索其核心原理。

Bean

Q1:spring的bean是单例的吗?

spring的bean的确是单例的,我们可以通过一个@scope注解来设置当前的bean是不是单例。如果我们不设置,默认的scope就是singleton(单例),如果设置为prototype就是多例的。

@Service
@Scope("singleton")
public class ExtFunctionServiceImpl implements ExtFunctionService {

    
}

Q2:单例bean是线程安全的吗?

先说结论,这个bean不是线程安全的。那么在开发过程中,这种bean会不会遇到什么问题呢?

我们来举一个例子:

@RestController
@RequestMapping("/user")
public class UseController {
    private int count;
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/getById/{id}")
    public User getById(@PathVariable("id") Integer id){
        count++;
        System.out.println(count);
        return userService.getById(id);
    }
}

如果有并发的请求过来,这段代码有没有线程安全问题呢?答案是肯定的,我们需要注意代码中的成员变量count,成员变量一个考虑线程安全问题。如果有并发请求过来,每一个请求都可以修改这个count,很明显是线程不安全的。

而getById方法中接收的参数,是一个形参,也就是局部变量。局部变量一般情况下是没有线程安全问题的。

代码中userService也是一个成员变量,它有没有线程安全问题呢?答案是没有。因为他是一个无状态的类,简单说就是这个成员变量是不能被修改的。无状态的类是没有线程安全问题的。从这个角度上说,spring的单例bean又是线程安全的。所以说我们在开发过程中尽可能不要去定义可修改的成员变量。

总的来说,一般在spring的bean中都是注入无状态的对象,没有线程安全问题,如果bean中定义了可修改的成员变量是要考虑线程安全问题的,可以使用多例或者加锁来解决。

Bean的生命周期

了解Bean的生命周期可以帮助我们更好的掌握Spring框架。

Spring容器在初始化的适合会将xml配置的<bean>的信息封装成一个BeanDefination对象,根据BeanDefination来创建Bean对象,里面有很多的属性用来描述Bean。

  • beanClassName:bean的类名
  • initMethodName:初始化方法名
  • properryValues:bean的属性值
  • scope:作用域
  •  lazyInit:延迟初始化

下面是BeanDefination类的一些方法:

比如其中getBeanName方法就是获取bean的类名,有了它再去创建bean对象的时候就可以通过反射来创建。这些属性都被封装到了BeanDefination中进行备用。

目前我们已经有了BeanDefination对象,想要创建bean对象,首先就要调用bean的构造函数,实例化当前bean对象。

第二步就是依赖注入,比如@Autowired或者@Value的属性,都是在这一步进行注入的。

第三步是Aware接口,比较常见的有图中的三个,如果bean实现了这些接口,就要实现里面的方法,比如BeanNameAware在bean的初始化过程中就可以获取bean的名称,在我们实际开发中用的比较少。

第四步是BeanPostProcessor,是一个bean后置处理器,用来增强bean的功能,在bean的初始化方法之前进行回调。这个后置处理器在bean的生命周期中起着重要的作用。

第五步就是初始化方法,初始化方法有两个,一个是InitializingBean,如果bean实现了这个接口就要重写里面的方法,这一步就是来执行重写之后的初始化方法的。另一个是自定义的初始化方法,比如在bean中某一个方法上使用@PostConstruct注解,就回去执行这个方法。

第六步还是一个BeanPostProcessor,是在初始化方法之后执行,当一个类被增强,使用AOP,那么这个类通常都是使用bean的后置处理器进行增强的。AOP底层使用的是动态代理,动态代理有两种:JDK和CGLB

到现在bean对象基本初始化完成,我们就可以去使用这个bean对象了。

注意,下面的几步都属于初始化赋值的过程,bean的创建和初始化是分开的。当容器关闭之后,bean对象就要执行销毁操作了。比如,在某个方法上使用@PreDestroy注解,那么这个方法就是一个销毁方法,spring容器在关闭的时候就会执行这个销毁方法。 

Bean的循环依赖

Bean的循环依赖指的是两个或多个Bean之间相互依赖,形成了闭环的依赖关系。具体来说,当两个或多个Bean互相引用对方作为依赖时,就会发生循环依赖。

 我们以上述这段代码为例分析一下死循环产生的过程:

首先我们要去实例化对象A,也就是在堆中开辟一块空间,此时A只是一个半成品,只调用了构造函数,还没有进行后续一系列的赋值初始化等操作。

下一步我们需要初始化A对象,在初始化的时候我们需要设置B属性,也就是把B注入到A中,此时bean会去spring容器在找,看是否有B对象,如果存在就直接赋值,当然目前并不存在。此时就需要实例化B对象,经理一样的流程,然而B对象在初始化的时候需要设置A属性,此时A对象也没有创建完成,只能又去实例化A,此时就出现了死循环。

 其实,Spring框架已经帮我们解决了大部分的循环依赖问题,我们来看看spring是如何解决的:

Spring通过三级缓存来解决循环依赖,对应三级缓存如下(部分):

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
    // 一级缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    // 三级缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
    // 二级缓存
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
}

DefaultSingletonBeanRegistry意为单实例对象的注册器,里面定义了三个集合,就是三级缓存。

缓存名称源码名称作用
一级缓存

singletonObjects

单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
二级缓存

earlySingletonObjects

缓存早期的bean对象(生命周期还没走完)
三级缓存

singletonFactories

缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的实例

当某个Bean被创建时,Spring首先将其放入singletonFactories缓存中,表示该Bean正在创建中。接着,Spring会尝试解析该Bean所依赖的其他Bean。如果其他Bean也正在创建过程中,Spring会从singletonFactories中获取对应的工厂实例,而不是直接返回未完全初始化的Bean。这样可以防止循环依赖导致的死锁情况发生。

如果其他Bean还未开始创建,那么Spring会继续递归地创建它们,并将它们放入singletonFactories缓存中。直到所有相关的Bean都创建完成,并且已经进行了属性注入。

接下来,Spring会将这些已经完成创建的Bean实例放入earlySingletonObjects缓存中,并执行依赖注入操作。然后,Spring会从singletonFactories和earlySingletonObjects缓存中移除这些Bean。

最后,Spring会将这些已经初始化好且可以被正常使用的Bean放入singletonObjects缓存中。

通过二级缓存,我们已经可以解决一般对象的循环依赖,如果一个对象被增强的话,就需要借助三级缓存了:

首先实例化对象A,然后A会生成一个ObjectFactory对象,把这个工厂对象放入三级缓存中。此时A需要注入B,B对象不存在,就需要实例化对象B,B对象也生成一个对象工厂放到三级缓存中。

对象B需要注入A,此时A虽然不存在,但是在三级缓存中有一个对象工厂ObjectFactory,这个ObjectFactory可以生成一个代理对象存入二级缓存并注入给B。 这样B就能创建成功放入单例池中,注入给A。

通过三级缓存的策略,Spring能够在创建Bean的过程中解决大部分循环依赖问题。当发生循环依赖时,Spring可以从缓存中获取到已经创建的Bean实例,而不会陷入死锁状态。这种机制保证了Bean的单例性以及正确的依赖注入,确保了循环依赖问题的解决和应用的正常运行。

然而,仍然有一些循环依赖问题是Spring无法解决的,需要我们手动去解决。比较典型的就是构造方法注入产生的循环引用问题。

与之前的代码不同之处是,之前的代码采用setter方法注入,这段代码采用的是构造器注入。我们回到之前bean的生命周期问题,构造方法是先于bean初始化执行的,spring的三级缓存只能解决初始化阶段的循环依赖问题,不能解决构造函数产生的循环依赖。

解决的方法很简单,只需要加一个@Lazy注解来延迟加载,什么时候需要才实例化。

public A(@Lazy B b){
    Ststem.out.println("A的构造方法执行了");
    this.b = b;
}

AOP

AOP被称为面向切面编程,用于将那些与业务无关,但却面对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

在我们的开发过程中用的最多的就是事务,Spring管理的事务其实底层用的就是AOP,而AOP的底层用的是动态代理。

常见AOP使用场景

1.记录操作日志

日志属于公共行为,比如项目中每个service都需要记录日志,我们不可能在每一个service中去编写日志的操作逻辑,这时使用AOP就非常方便。

在我们平时的后台开发中,都有记录操作日志的需求。当用户操作系统的某一个请求的时候,需要记录请求的用户,方式,访问地址等等,模块的名称,登陆的IP,操作时间都需要记录到日志表当中。

下面我,我们可以分析一下具体的实现方式。

假设我们的用户请求到达后端以后,后端一共有四个接口:登录,新增用户,更新用户,删除用户。当有这些操作的时候,我们就需要操作日志了。

我们以新增用户举例,我们可以使用AOP提供的环绕通知在这里面做一个切面,切面就相当于是一个通用的代码。

在这里我们提供了一个写好切面类用作参考。

@Aspect
@Component
public class LoggingAspect {

    private final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Pointcut("execution(* com.example.myapp.*.*(..))")
    public void applicationPackagePointcut() {
        // 定义一个切入点,限定需要记录日志的方法所在的包路径
    }

    @Before("applicationPackagePointcut()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("前置通知:开始执行方法:" + joinPoint.getSignature().getName());
    }

    @AfterReturning(pointcut = "applicationPackagePointcut()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("后置通知:方法 " + joinPoint.getSignature().getName() + " 执行完毕");
        logger.info("返回结果:" + result);
    }

    @AfterThrowing(pointcut = "applicationPackagePointcut()", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable exception) {
        logger.error("异常通知:方法 " + joinPoint.getSignature().getName() + " 抛出了异常");
        logger.error("异常信息:" + exception.getMessage());
    }

    @Around("applicationPackagePointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取用户名
        // 需要通过解析session或token获取

        // 获取被增强类方法和信息
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 获取被增强的方法和对象
        Method method = methodSignature.getMethod();
        // 从方法中解析注解
        if (method != null) {
            Log logAnnotation = method.getAnnotation(Log.class);
            System.out.println(logAnnotation.name());
        }
        // 方法名字
        String name = method.getName();
        System.out.println(name);

        // 通过工具类获取Request对象
        RequestAttributes reqa = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) reqa;
        HttpServletRequest request = sra.getRequest();
        // 访问的url
        String url = request.getRequestURI().toString();
        System.out.println(url);
        // 登录IP
        String ipAddr = getIpAddr(request);
        System.out.println(ipAddr);
        //操作时间
        System.out.println(new Date());
        // 保存到数据库
        // ...
        return joinPoint.proceed();
    }

    private String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equals(ip)) {
            ip = request.getHeader("Proxy=client-IP");
        }
        return ip;
    }

}

 首先,当前类必须被Spring进行管理,@Component注解是需要的。@Aspect注解标明这是一个切面类。

最下面加了@Around注解的方法就是我们定义的环绕通知,方法中有一个形参joinPoint,通过joinPoint我们可以获取当前被增强的类和方法的信息,这是通过上面的pointcut(),也就是切点表达式,切点表达式是通过@Annotation注解,功能为:当前某一个方法如果加了Log注解,这个方法就会进入到这个环绕通知进行增强。

比方说我们的Controller层中的某个方法加了Log注解:

    @GetMapping("/getById/{id}")
    @Log(name = "根据用户id获取用户")
    public User getById(@PathVariable("id") Integer id){
        return userService.getById(id);
    }

其中Log是我们自定义的注解

@Target({ElementType.PARAMETER,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 模块名称
     */
    public String name() default "";
}

Log当中有一个属性是name,也就是模块名称,需要我们去说明,比如当前的getById就是根据用户id获取用户。

回到切面类,只要方法中有这个Log注解,都会到logArround方法中进行执行。在logArround方法中,我们首先找到被增强的类和方法的信息(目前是UserController,getUserById),接下来获取到方法的对象,有了对象之后我们就可以从方法对象中去获取当前的注解了,看看是否有Log注解,如果有我们就取出里面的name信息,也就是“根据用户id获取用户”

接下来我们通过工具类找到了请求对象,也急速Request,获取url,请求方式等,有了这些信息就可以保存到数据库中,就算是记录日志完成了。

2.缓存处理

如果我们某些业务需要加缓存,直接写在service中也会有耦合的情况,我们就可以使用AOP的切面,拦截需要添加缓存的业务方法。

        

3.Spring中内置的事务处理

Spring中提供了两种事务管理的方式:编程式事务管理和声明式事务管理。

  • 编程式事务控制:需使用TransactionTemplate来进行实现,需要在代码中进行事务的开启,提交,回滚,对业务代码有侵入性,项目很少使用
  • 声明式事务管理:本质是通过AOP进行拦截,将事务处理的功能贬值到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或回滚事务。

比如我们需要在业务中去保存用户,我们使用了AOP的切面:

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint)throws Throwable{
        try{
            // 开启事务
            // 执行业务代码
            Object proceed=joinPoint.proceed();
            // 提交事务
            return proceed;
        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
        }
        return null;
    }

其中:

Object proceed=joinPoint.proceed();这一行就是真正去执行目标方法(比如保存用户),我们在目标方法执行之前开启事务,目标方法执行之后提交事务,如果代码出错,就在catch中回滚事务。

在具体操作中,我们需要在保存用户的方法上或类上加一个@Transactional注解。

Spring事务失效的场景

这个问题需要我们对Spring框架的深入理解以及复杂业务的编码经验。事务失效的场景有很多,在这里我们例举三种比较常见的场景:

1.异常捕获处理

我们先来看一段代码:

    @Transactional
    public void update(Integer from,Integer to,Double money){
        try {
            // 转账的账户不能为空
            Account fromAccount=accountDao.selectById(from);
            // 判断用户的钱是否够转账
            if(fromAccount.getMoney()-money>=0){
                fromAccount.setMoney(fromAccount.getMoney()-money);
                accountDao.updateById(fromAccount);
            }
            // 异常
            int a=1/0;

            Account toAccount=accountDao.selectById(to);
            toAccount.setMoney(toAccount.getMonney()+money);
            accountDao.updateById(toAccount);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

这是一段用户转账的代码,这里就有可能会出现事务失效的场景。

原因在于异常被捕获后未重新抛出,导致事务无法回滚。

在Spring中,使用@Transactional注解来标记一个方法为事务性方法。当方法执行过程中发生了未被捕获的异常时,Spring会自动回滚事务。但是在你的代码中,异常被捕获后只是进行了打印输出,并没有重新抛出异常或手动设置事务回滚。

如果想要进行事务回滚,我们可以在e.printStackTrace();后面再次throw e抛出异常。

2.抛出检查异常

    @Transactional
    public void update(Integer from,Integer to,Double money) throwsFileNotFoundException{
        try {
            // 转账的账户不能为空
            Account fromAccount=accountDao.selectById(from);
            // 判断用户的钱是否够转账
            if(fromAccount.getMoney()-money>=0){
                fromAccount.setMoney(fromAccount.getMoney()-money);
                accountDao.updateById(fromAccount);
            }
            // 读取文件
            new FileInpputStream("ddd");

            Account toAccount=accountDao.selectById(to);
            toAccount.setMoney(toAccount.getMonney()+money);
            accountDao.updateById(toAccount);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

这段代码还是转账的代码,但是异常抛出的情况不一样,我们读取了一个不存在的文件,throw了文件找不到的异常,这种情况也会导致事务失效。这是因为spring默认只会回滚非检查异常,也就是RuntimeException,如果想要捕获检查异常,我们可以在@Transactional注解后面添加属性来实现:@Transactional(rollbackFor=Exception.class),这样只要有异常,事务就会回滚。

3.非public方法

如果我们的方法没有使用public修饰,就会导致事务失效。这是因为spring为方法创建代理,增加事务通知的前提条件都是该方法是public的。

SpringMVC

执行流程

SpringMvc的执行流程是这个框架最核心的问题,目前这个执行流程分为两种情况:

  • 视图阶段:JSP等老旧项目
  • 前后端分离阶段:接口开发异步请求,只需要响应

视图阶段流程:

假设现在有一个请求:http://localhost:8080/user/getById/1

请求从浏览器发送到后台,后台会有一个前端控制器来接收请求,这个控制器就是DispatcherServlet,相当于一个调度中心,所有的请求都要先经过他,他是被Tomcat容器初始化的,当这个类被加载之后,在内部就加在了一些组件类。比如HandlerMapping(处理器映射器),HandlerAdaptor(处理器适配器),ViewResolver(视图解析器)。

假设请求已经到了前端控制器,首先会先走第一个组件HandlerMapping,这里保存的信息是当前的控制器以及handler信息(当前某一个控制器中的某一个方法),HandlerMapping解析完成之后会返回给前端控制器一个HandlerExecutionChain(处理器执行链),因为执行某一个方法的时候有可能会出现拦截器,如果有拦截器,HandlerMapping就会把当前拦截器和要执行的方法名一块封装到当前的处理器执行链。

如果没有拦截器,DispatcherServlet再去找当前的HandlerAdaptor去执行handler,最终HandlerAdaptor再去找具体的方法去执行。此时Handler还是指我们某一个控制器中的某一个方法,方法执行完之后返回给HandlerAdaptor,在此过程中HandlerAdaptor做了两个非常重要的事:处理参数和返回值。

@RestController
@RequestMapping("/user")
public class UseController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/getById/{id}")
    public User getById(@PathVariable("id") Integer id){
        return userService.getById(id);
    }
}

我们还是来看这段代码,每个方法都有自己的参数,每一个参数的类型都有可能不同的,要处理这些参数就需要HandlerAdaptor了,HandlerAdaptor中有一些对应的处理参数的类型转换器,最终让我们的方法正确的接收这些参数。

返回值也有多种不同类型,返回值返回给前端也是HandlerAdaptor来处理的。

Handler执行完成之后会返回一个ModelAndView给当前的前端控制器给HandlerAdaptor,HandlerAdaptor再把ModelAndView返回给前端控制器进行处理,前端控制器接收到ModelAndView再去找视图解析器,最终视图解析器将逻辑视图转换为真正的视图(View)返回给前端控制器,渲染视图给JSP。

然而,时代在发展社会在进步,随着前后端分离开发的普及,很多接口返回的都是json而不是ModelAndView,这就是我们的流程发生了改变。

前后端分离阶段

前端控制器接收到请求之后先调用HandlerMapping找到对应方法去执行,在调用HandlerAdaptor让他去执行具体的Handler,再然后Handler在某一个方法上加一个@ResponseBody注解,此时这个方法的返回值就会被HttpMessageConverter转成json并响应。

Springboot

自动配置原理

自动配置是Springboot框架最核心的思想。我们通过Springboot启动类的代码来分析Springboot自动配置的原理:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

这就是一个普通的引导类代码,启动配置主要依赖@SpringBootApplication注解,这个注解中还包含了另外三个注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
  • @SpringBootConfiguration:该注解与@tConfiguration作用相同,用来声明当前也是一个配置类。

  • @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包。

  • @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解。(详细见下文)

我们打开@EnableAutoConfiguration的源码,其内部结构是这样的:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@Import({AutoConfigurationImportSelector.class})意为导入自动配置的选择器。在这里SpringBoot会加载一个文件,也就是spring.factories,将这个文件的内容同意加到spring容器中。

 这个文件中好多的类都是以AutoConfiguration结尾的,自动配置的类。这里面的自动配置类并不是所有都会加载进来:

我们从中选择一个redis的AutoConfiguration:

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
    public RedisAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
}

这里面又有很多的注解:

  • @ConditionalOnClass:判断是否有对应的字节码RedisOperations.class,当我们导入了redis相关的启动依赖之后,RedisOperations.class就存在了,class存在就回家再当前的RedisAutoConfiguration类,放到Spring容器中。

  • @Bean:将当前方法的返回值放到Spring容器中,在自己的业务层,我们就可以使用@Autowired去自动注入RedisTemplate了。

  • @ConditionalOnMissingBean(name = {"redisTemplate"}):判断当前对象有没有被创建,如果已经存在就不用再创建了。

常见注解

Spring常见注解

注解说明
@Component,@Controller,@Service,@Repository使用在类上用于实例化bean

@Autowired

使用在字段上进行依赖注入
@Qualifier结合@Autowired一起使用根据名称进行依赖注入
@Scope标注Bean作用域
@Configuration用于将一个类标记为配置类,创建容器时会从该类上加载注解
@ComponentScan用于指定Spring容器初始化时要扫描的包
@Bean使用在方法上,标注将该方法的返回值存储到spring的容器中。
@Import使用@Import导入到类会被Spring加载到IOC容器中
@Aspect,@Before,@After.@Around,@Pointcut用于切面编程(AOP)

SpringMVC常见注解

注解说明

@RequestMapping

用于映射请求路径,用于类或者方法上
@RequestBody注解实现接收http请求的json数据,将json转换成java对象
@RequestParam指定请求参数的名称
@PathVariable从路径下获取请求参数{/user/{id}},传递给方法的形式参数
@ResponeseBody注解实现将controller方法返回对象转化为json对象相应给客户端
@RequestHeader获取指定的请求头数据
@RequestController@Controller+@ResponeseBody

Springboot常见注解

注解说明
@SpringBootConfiguration组合了@Configuration注解
@EnableAutoConfiguration打开了自动配置的功能,也可以关闭某个自动配置的选项
@ComponentScanSpring组件扫描

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值