前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring学习笔记(2)一DI依赖注入和Spring Bean配置、注解原理、动态注入

Spring学习笔记(2)一DI依赖注入和Spring Bean配置、注解原理、动态注入

作者头像
黄规速
发布2022-04-14 16:02:43
7810
发布2022-04-14 16:02:43
举报
文章被收录于专栏:架构师成长之路

一、IOC和依赖注入DI

Spring容器是Spring框架的核心。容器将创建对象,它们连接在一起,配置它们,并从创建到销毁管理他们的整个生命周期。在Spring容器使用依赖注入(DI)来管理组成应用程序的组件。这些对象被称为Spring Beans。 IOC(Inversion of Control):传统的方法,当某个java对象A需要调用对象B时,是由调用者(对象A)通过new关键字来创建对象B的,而在Spring中,则是由spring完成创建的,所以“控制反转”了。Spring通过一种称作控制反转(IoC)的技术促进了低耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI(JNDI是 Java 命名与目录接口Java Naming and Directory Interface))相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。 DI(Dependency Injection)—IOC的另一种表述方式:即组件以一些预先定义好的方式(例如: setter方法)接受来自如容器的资源注入.相对于 IOC而言,这种表述更直接。

注入可以理解为是对一个对象进行初始化,也就是省去new的这个步骤,类似于工厂模式一样,通过一个工厂制造出这个对象,如果遇到修改,只需要改一处就行了。实现spring注入一般有两种方法,配置文件,或者用注解。各有不同的作用。

容器获得其上的哪些对象进行实例化,配置和组装通过阅读提供的配置元数据的说明。配置元数据可以通过XML,Java注释或Java代码来表示。下面的图是Spring如何工作的高层次图。 Spring IoC容器是利用Java的POJO类和配置元数据的产生完全配置和可执行的系统或应用程序。

二、Spring容器


Spring提供了以下两种不同类型的容器。

1、Spring BeanFactory 容器:

这是最简单的容器DI提供基本的支持和定义由org.springframework.beans.factory.BeanFactory 接口. BeanFactory或者相关的接口,例如实现BeanFactoryAware,InitializingBean,DisposableBean,仍然存在在Spring向后兼容性与大量的与Spring整合第三方框架的目的。

2、Spring ApplicationContext 容器

此容器添加了更多的企业特定的功能,例如从一个属性文件解析文本消息的能力,并发布应用程序事件感兴趣的事件监听器的能力。此容器是由 org.springframework.context.ApplicationContext 接口定义.

在ApplicationContext 容器包括BeanFactory的容器的所有功能,所以因此通常建议在BeanFactory。 BeanFactory仍然可以用于重量轻的应用,如移动装置或基于小应用程序的应用中的数据量和速度是显著。

实例化一个spring容器,容器会自动预初始化所有Bean实例。

代码语言:javascript
复制
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //实例化一个Spring容器,
Boss boss = (Boss) ac.getBean("boss"); //取得对象boss

三、Spring Bean配置

bean定义包含所需要的容器要知道以下称为配置元数据的信息:

1、如何创建一个bean 2、Bean 生命周期的详细信息 3、Bean 依赖关系

上述所有配置元数据转换成一组的下列属性构成每个bean的定义。

属性

描述

class

此属性是强制性的,并指定bean类被用来创建bean。

name

此属性指定唯一bean标识符。在基于XML的配置元数据时,您可以使用id和/或name属性来指定bean标识符

scope

该属性指定一个特定的bean定义创建,它会在bean作用域本章要讨论的对象范围。

constructor-arg

这是用来注入的依赖关系,并在接下来的章节中进行讨论。

properties

这是用来注入的依赖关系,并在接下来的章节中进行讨论。

autowiring mode

这是用来注入的依赖关系,并在接下来的章节中进行讨论。

lazy-initialization mode

延迟初始化的bean告诉IoC容器创建bean实例时,它首先要求,而不是在启动时。

initialization method

回调只是在bean的所有必要属性后调用已设置的容器。它会在bean的生命周期章节中讨论。

destruction method

当包含该bean容器被销毁所使用的回调。它会在bean的生命周期章节中讨论。

Spring IoC容器完全由在此配置元数据实际写入的格式解耦。有下列提供的配置元数据的Spring容器三个重要的方法:

  1. 基于XML的配置文件。
  2. 基于注解的配置
  3. 基于Java的配置

bean创建的几个重要步骤: 1) 创建bean实例,也就是bean的实体化,创建过程不仅仅只有java的反射机制,还结合了动态代理的方式 2) 记录创建bean的objectFactory 3) 属性注入 4) 初始化bean 5) 注册disposablebean

Spring bean的生命周期:

四、Spring bean的XML配置

Spring配置文件是一个或多个标准的XML文档,applicationContext.xml是Spring的默认配置文件,当容器启动时找不到指定的配置文档时,将会尝试加载这个默认的配置文件。

下面列举的是一份比较完整的配置文件模板,文档中各XML标签节点的基本用途也给出了详细的解释

1、import导入

在我们的beans.xml中就可以引入和使用entity.xml文件中的bean配置(代码中config/spring为我们新建的包config.spring)

代码语言:javascript
复制
<import resource="config/spring/entity.xml"/>

Bean的作用域 scope指bean的作用域,在配置bean时,有scope属性来配置bean的作用 注意:在整合struts和spring时,需要将action设为scope="prototype";

代码语言:javascript
复制
 <bean id="addr" class="cn.sxt.vo.Address" scope="singleton">
    <!-- scope:bean的作用域 (默认是singleton):
    singleton:单例(用计数器记录),整个容器中只有一个对象的实例;
    prototype原型:每次获取bean都产生一个新的对象;
    request:每次请求时,创建一个新的对象;
    session:在回话的范围内是一个对象(servlet的session);
    global session: 只在portlet下有用,表示是application
    application:在应用范围内,只有一个对象;
    -->

2.设置注入:

属性必须写setter方法。格式为: 要求被注入的属性必须有set方法。set方法的方法名由set+属性(属性首字母大写),如果属性是boolean类型,没有get方法(是is);

代码语言:javascript
复制
public class Student {
    private String name;
    public void setName(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("name="+name);

    }
}
代码语言:javascript
复制
 <bean id="student" class="cn.sxt.vo.Student">
        <property name="name" value="hgs"></property>
    </bean>
代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
        Student stu=(Student)ac.getBean("student");
        stu.show();
    }

}

2.构造注入:必须写有参的构造函数。

<bean id="car" class="wang.spring.Car "> <constructor-arg index=" 0" type=“int” value=" 001” /> <constructor-arg index=" 1" type=“String” value=" baoma” /> </bean>

利用上面代码,可以新建一个实例car,并给它的属性赋值。

3.依赖注入:当引用了其他类的对象时。

<property name=" " ref=" ">或者<constructor-arg ref=" ">

代码语言:javascript
复制
public class Address {
    private String address;
    public String getAddress() {
        return address;

    }

    public void setAddress(String address) {
        this.address = address;

    }
}

Student类中引用Address类的对象(Student的实例变量是另外一个类Address的对象):

代码语言:javascript
复制
private Address addr;
    public void setAddr(Address addr) {
        this.addr = addr;
}

beans.xml:

代码语言:javascript
复制
 <bean id="addr" class="cn.sxt.vo.Address">
        <property name="address" value="北京海淀"/>
    </bean>
    <bean id="student" class="cn.sxt.vo.Student">
        <property name="name" value="hgs"></property>
        <property name="addr" ref="addr"></property>
    </bean>

4.集合的注入:

代码语言:javascript
复制
public class Student {
    private String name;
    public void setName(String name) {
        this.name = name;
    }

    private String[] books;
    public void setBooks(String[] books) {// 数组注入
        this.books = books;
    }

    private List<String> hobbies;
    public void setHobbies(List<String> hobbies) {//List注入
        this.hobbies = hobbies;
    }

    private Map<String, String> cards;
    public void setCards(Map<String, String> cards) {//map注入
        this.cards = cards;
    }
    private Set<String> games;
    public void setGames(Set<String> games) { //set注入
        this.games = games;
    }

    private Properties info;
    public void setInfo(Properties info) {//属性注入
        this.info = info;
    }
}

beans.xml做以下配置

代码语言:javascript
复制
<bean id="student" class="cn.sxt.vo.Student">
        <property name="name" value="张三丰"></property>
        <property name="addr" ref="addr"></property>
        <property name="books">
            <array>
                <value>傲慢与偏见</value>
                <value>仲夏夜之梦</value>
                <value>雾都孤儿</value>
            </array>
        </property>
        <property name="hobbies">
            <list>
                <value>羽毛球</value>
                <value>乒乓球</value>
                <value>玻璃球</value>
                <value>排球</value>
            </list>
        </property>
        <property name="cards">
            <map>
                <entry key="中国银行" value="1545615345415"></entry>
                <entry>
                    <key><value>农业银行</value></key>
                    <value>54654861231543</value>
                </entry>
            </map>
        </property>
        <property name="games">
            <set>
                <value>LOL</value>
                <value>dota</value>
                <value>cs</value>
                <value>dnf</value>
                <value>cf</value>
                
            </set>
        </property>
        <property name="wife"><null/></property>
        <property name="info">
        <props>
            <prop key="学号">2015534001</prop>
            <prop key="sex">男</prop>
            <prop key="name">张三</prop>
        </props>
        </property>
    </bean>

5.自动注入(spring4):

是用来简化spring的配置文件,在配置bean时,可以配置bean的autowire属性,用于指定注入类型

代码语言:javascript
复制
<bean id="userDao" class="cn.sxt.dao.impl.UserDaoMySqlImpl"></bean>
    <!-- autowire:自动装配,用于简化spring的配置
         no     不使用自动装配
         byname 根据名称(set方法名)来查找相应的bean,如果有则装配上去
           使用形式:xml头文件最后面加上default-autowire="byName"
         byType 根据类型进行装配,不同去管bean的id(bean的id可以写任意)
                 但是同一种类型的bean只能有一个(尽量慎用byType)
         constructor 当使用构造器实例化bean时,适用byType的方式装配构造方法
     -->
    <bean id="service" class="cn.sxt.service.impl.UserServiceImpl" autowire="constructor"></bean>

可以配置全局的自动装配类型,在xml文件的头部:default-autowire="byname"

【注意】推荐不使用自动装配,而使用annotation

五、Spring bean注解配置

注解配置相对于 XML 配置具有很多的优势:

  • 它可以充分利用 Java 的反射机制获取类结构信息,这些信息可以有效减少配置的工作。如使用 JPA 注释配置 ORM 映射时,我们就不需要指定 PO 的属性名、类型等信息,如果关系表字段和 PO 属性名、类型都一致,您甚至无需编写任务属性映射信息——因为这些信息都可以通过 Java 反射机制获取。
  • 注释和 Java 代码位于一个文件中,而 XML 配置采用独立的配置文件,大多数配置信息在程序开发完成后都不会调整,如果配置信息和 Java 代码放在一起,有助于增强程序的内聚性。而采用独立的 XML 配置文件,程序员在编写一个功能时,往往需要在程序文件和配置文件中不停切换,这种思维上的不连贯会降低开发效率。

因此在很多情况下,注释配置比 XML 配置更受欢迎,注释配置有进一步流行的趋势。

1、从Spring 3.x开始,Spring被外界最为诟病的一点就是配置繁多,号称“配置地狱”,各种xml文件,出了问题非常难排查。 2、从Spring 4.x开始,Spring.io提供了三种方式编织Bean: 1)利用注解:隐式配置,例如:@Autowired、@Bean、@Component等,通过注解来简化xml文件。 2)利用Java文件:显示配置,比xml配置的优势是具备类型安全 3)利用传统的xml配置文件 3、在Spring Boot中几乎可以完全弃用xml配置文件。

注解可以分为两大类。JSR-250规范注解方式 和 Spring自带的注解方式。

通过注解的方式装配时,必须在配置文件中添加一个bean,它其实是一个注解处理器,用于解析注解。

JSR-250规范注解方式的处理器:

代码语言:javascript
复制
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>

Spring自带的注解方式的处理器:

<bean class="org.sprinframework.beans.factory.annotation. AutowiredAnnotationBeanPostProcessor"/>

当然,我们有更方便的方法,两种注解方式都可以用下面的方式隐式得注册注解处理器:

<context:annotation-config>

Spring提供如下几个Annotation来标注Spring Bean:

@Component标注一个普通的Spring Bean; @Controller:标注一个控制器组件类; @Service:标注一个业务逻辑组件类; @Repository:标注一个Dao组件; 这时,bean.xml文件中就不必用<bean>来定义bean了。

1、注解配置bean的创建

我们先了解@Controller

Spring容器框架包org.springframework.stereotype下Controller注解接口源代码如下

代码语言:javascript
复制
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    String value() default "";
}

@Target({java.lang.annotation.ElementType.Type})表是注解使用在类上; @Retention(RetentionPolicy.RUNTIME)表示注解在程序运行状态下还有效; @Component表示该类对象以默认单例模式存放在spring容器框架中; @Documented无关重要,略;

其他的注解@Respository,@Service等,这两个注解的源代码和Controller的源代码一样,除了接口名,都共有一个value抽象函数。

这些所有的注解都会被component注解接口注解,所有“继承”了component注解接口的注解修饰用户的类会被spring中的注解处理器获取(getAnonations()),判定存在component注解后,注解处理器会在spring容器框架中根据用户类的全限定名通过java的反射机制创建这个用户类的对象,并放到spring容器框架中进行管理。

2、如何找到这些bean的类文件

说完bean的创建过程,那spring是如何找到这些bean的类文件的呢?

我们在spring的配置文件中,添加一行<context:component-scan base-package="com.spring.demo"/>用来指定这些类所在的包。标签context:component-scan的属性base-package中设置要扫描的包。spring框架中肯定有根据base-package属性扫描得到所有需要管理的bean对象,这个节点中的所有属性会被放入扫描模块对象工具中去,结果就是将所有的bean对象放到spring的容器中去。

1)扫描类文件:根据配置利用asm技术扫描.class文件,并将包含@Component及元注解为@Component的注解@Controller、@Service、@Repository 或者还支持Java EE 6的@link javax.annotation.ManagedBean和jsr - 330的 @link javax.inject.Named,如果可用。的bean注册到beanFactory中

2)是处理属性或方法中的注解 注册@Configuration处理器ConfigurationClassPostProcessor, 注册@Autowired、@Value、@Inject处理器AutowiredAnnotationBeanPostProcessor, 注册@Required处理器RequiredAnnotationBeanPostProcessor、在支持JSR-250条件下注册javax.annotation包下注解处理器CommonAnnotationBeanPostProcessor,包括@PostConstruct、@PreDestroy、@Resource注解等、支持jpa的条件下,注册org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor处理器,处理jpa相关注解注册@EventListener处理器EventListenerMethodProcessor 注解处理器的的实例化和处理器的注册时同步的,实例化后放入到beanFactory的beanProcessors列表中去。

Spring框架的核心就是IOC,通过controller一类注解的bean的实例化过程。

可以大体总结spring注解的工作原理:

1)利用asm技术扫描class文件,转化成Springbean结构,把符合扫描规则的(主要是是否有相关的注解标注,例如 @Component)bean注册到Spring 容器中beanFactory 2)注册处理器,包括注解处理器 4)实例化处理器(包括注解处理器),并将其注册到容器的beanPostProcessors列表中 5)创建bean的过程中,属性注入或者初始化bean时会调用对应的注解处理器进行处理。

SpringBoot项目的Bean装配默认规则是根据Application类(SpringBoot项目入口类)所在的包位置从上往下扫描!这个类的位置很关键:例如Application类所在的包为:com.tl. projec.app,则只会扫描com.tl. projec.app包及其所有子包,如果service或dao所在包不在com.tl. projec.app及其子包下,则不会被扫描! 解决方案: 1.将SpringBoot项目的启动类,放在所以包之上。如Application类放在:“com.tl. project”包下,其他类放在“com.tl. projec.bean”、“com.tl. projec.controller”、“com.tl. projec.dao”等。 2.使用@ComponentScan注解,在springboot的启动类上注解配置,需要扫描的包,例如: @ComponentScan(basePackages={" com.tl. projec.app"," com.tl. projec.bean "}) 注意:经测试,依赖注入,不能向static属性注入Spring上下文中的对象。

3、bean注入

我们可以自由地使用任何标准的Spring框架技术去定义beans和它们注入的依赖。 简单起见, 我们经常使用 @ComponentScan 注解搜索beans, 并结合 @Autowired 构造器注入。 如果使用上面建议的结构组织代码( 将应用类放到根包下) , 你可以添加 @ComponentScan 注解而不需要任何参数。 你的所有应用程序组件( @Component , @Service , @Repository , @Controller 等) 将被自动注册为Spring Beans。

1 )、构建器注入

下面是一个 @Service Bean的示例, 它使用构建器注入获取一个需要的 UserDao bean。

代码语言:javascript
复制
package com.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DatabaseAccountService implements AccountService {
	private final UserDao userDao;
	@Autowired
	public DatabaseAccountService(UserDao userDao) {
		this.userDao = userDao;
	} 
	// ...
}

注:注意如何使用构建器注入来允许 userDao 字段被标记为 final , 这意味着 userDao 后续是不能改变的。

2)、直接通过@Autowried 注解注入

直接通过@Autowried 注解对Service对象进行注解即可

在Controller中: @Controller @RequestMapping("/test") public class ExampleController { @Autowired private ExampleService service; }

在Service中 @Component public class ExampleServiceImpl Implements ExampleService { @Autowired private ExampleDao exampleDao; }

六、常用Spring注解

常用注解(annotations)列表 @ResponseBody :用该注解修饰的函数,会将结果直接填充到HTTP的响应体中,一般用于构建RESTful的api; @Controller :用于定义控制器类,在spring 项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层)。 @RestController :@ResponseBody和@Controller的合集 @RequestMapping :提供路由信息,负责URL到Controller中的具体函数的映射。 @EnableAutoConfiguration :Spring Boot自动配置(auto-configuration):尝试根据你添加的jar依赖自动配置你的Spring应用。例如,如果你的classpath下存在HSQLDB,并且你没有手动配置任何数据库连接beans,那么我们将自动配置一个内存型(in-memory)数据库”。你可以将@EnableAutoConfiguration或者@SpringBootApplication注解添加到一个@Configuration类上来选择自动配置。如果发现应用了你不想要的特定自动配置类,你可以使用@EnableAutoConfiguration注解的排除属性来禁用它们。例子代码如下: @ComponentScan :表示将该类自动发现(扫描)并注册为Bean,可以自动收集所有的Spring组件,包括@Configuration类。我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。 @Configuration :相当于传统的xml配置文件,如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。 @SpringBootApplication :相当于@EnableAutoConfiguration、@ComponentScan和@Configuration的合集。 @Import :用来导入其他配置类。 @ImportResource :用来加载xml配置文件。 @Autowired :自动注入,自动导入依赖的bean。自动从spring的上下文找到合适的bean来注入 @Service :一般用于修饰service层的组件 @Repository :使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。 @Qualifier 或者 @Resource : @Autowired 和 @Qualifier 或者 @Resource 配合使用,指定bean的名称,这样spring就知道你的接口要调用那个实现类了。 上面的Autowired和Resource是用来修饰字段,构造函数,或者设置方法,并做注入的。而Service,Controller,Repository,Component则是用来修饰类,标记这些类要生成bean。

具体说明:

1:@Component @Component是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用。

2:@Controller @Controller对应表现层的Bean,也就是Action,例如:

代码语言:javascript
复制
@Controller  
@Scope("prototype")  
public class UserController {  
  
} 

注:实际上,使用@component,也可以起到@Controller同样的作用。

使用@Controller注解标识UserController之后,就表示要把UserController交给Spring容器管理,在Spring容器中会存在一个名字为"UserController"的action,这个名字是根据UserController类名来取的。注意:如果@Controller不指定其value【@Controller】,则默认的bean名字为这个类的类名首字母小写,如果指定value【@Controller(value="UserController")】或者【@Controller("UserController")】,则使用value作为bean的名字。 这里的UserController还使用了@Scope注解,@Scope("prototype")表示将Action的范围声明为原型,可以利用容器的scope="prototype"来保证每一个请求有一个单独的Action来处理,避免struts中Action的线程安全问题。spring 默认scope是单例模式(scope="singleton"),这样只会创建一个Action对象,每次访问都是同一Action对象,数据不安全,struts2 是要求每次次访问都对应不同的Action,scope="prototype" 可以保证当有请求的时候都创建一个Action对象。

3:@RequestMapping

@RequestMapping是一种通过匹配URL路径来访问相应页面的,@RequestMapping 注解将类似 “/user”这样的URL映射到整个类或特定的处理方法上。一般来说,类级别的注解映射特定的请求路径到表单控制器上,而方法级别的注解只是映射为一个特定的HTTP方法请求(“GET”,“POST”等)或HTTP请求参数。

代码语言:javascript
复制
@Controller  
@RequestMapping("/user")  
public class UserController {  
    @RequestMapping(value = "/list", method = {RequestMethod.GET,RequestMethod.POST})  
	public String list(HttpServletRequest request) {  
  
	}  
}  

4:@Autowired

将 @Autowired 注释标注在成员变量上 ,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。

@Autowired 根据bean 类型从spring 上线文中进行查找,注册类型必须唯一,否则报异常。与@Resource 的区别在于,@Resource 允许通过bean 名称或bean 类型两种方式进行查找@Autowired(required=false) 表示,如果spring 上下文中没有找到该类型的bean 时, 才会使用new SoftPMServiceImpl(); @Autowired 标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。 @Autowired 还有一个作用就是,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。

代码语言:javascript
复制
@Controller  
@RequestMapping("/user")  
public class UserController {  
    @Autowired  
    private UserBiz userBiz;  
}  

5:@RequestParam

@RequestParam将请求的参数绑定到方法中的参数上。其实,即使不配置该参数,注解也会默认使用该参数。如果想自定义指定参数的话,如果将@RequestParam的 required 属性设置为false(如@RequestParam(value="id",required=false))。

6:@RequestBody

@RequestBody是指方法参数应该被绑定到HTTP请求Body上。

代码语言:javascript
复制
@RequestMapping(value = "/user",method = RequestMethod.GET)  
public void UserInfo(@RequestBody String userId,User user){  
      
}  

7:@ResponseBody

@ResponseBody与@RequestBody类似,它的作用是将返回类型直接输入到HTTP response body中。最常用的我们使用ajax传输json,需要再类上面配置@ResponseBody。

代码语言:javascript
复制
@ResponseBody  
@RequestMapping(value = "/user", method = RequestMethod.POST)  
public String list() {      
    return "Hello World";  
}  

8:@ModelAttribute @ModelAttribute可以作用在方法或方法参数上,当它作用在方法上时,标明该方法的目的是添加一个或多个模型属性(model attributes)。该方法支持与@RequestMapping一样的参数类型,但并不能直接映射成请求。控制器中的@ModelAttribute方法会在@RequestMapping方法调用之前而调用,示例如下:

代码语言:javascript
复制
@ModelAttribute  
public User addUser(@RequestParam String userId) {  
    ...  
}  

@ModelAttribute方法用来在model中填充属性,如填充下拉列表、宠物类型或检索一个命令对象比如账户(用来在HTML表单上呈现数据)。 @ModelAttribute方法有两种风格:一种是添加隐形属性并返回它。另一种是该方法接受一个模型并添加任意数量的模型属性。用户可以根据自己的需要选择对应的风格。

@ModelAttribute作用在方法参数上

当@ModelAttribute作用在方法参数上时,表明该参数可以在方法模型中检索到。如果该参数不在当前模型中,该参数先被实例化然后添加到模型中。一旦模型中有了该参数,该参数的字段应该填充所有请求参数匹配的名称中。这是Spring MVC中重要的数据绑定机制,它省去了单独解析每个表单字段的时间。 @ModelAttribute是一种很常见的从数据库中检索属性的方法,它通过@SessionAttributes使用request请求存储。在一些情况下,可以很方便的通过URI模板变量和类型转换器检索属性。 9:@Cacheable 和@CacheFlush

@Cacheable :声明一个方法的返回值应该被缓 存 例如:@Cacheable(modelId = "testCaching") @CacheFlush :声明一个方法是清空缓存的触发器 例如:@CacheFlush(modelId = "testCaching")

10:@Resource

@Resource 默认按bean 的name 进行查找,如果没有找到会按type 进行查找, 此时与@Autowired 类似在没有为 @Resource 注解显式指定 name 属性的前提下,如果将其标注在 BeanFactory 类型、ApplicationContext 类型、ResourceLoader 类型、 ApplicationEventPublisher 类型、MessageSource 类型上,那么 Spring 会自动注入这些实现类的实例,不需要额外的操作。此时 name 属性不需要指定 ( 或者指定为""),否则注入失败。

11:@PostConstruct 和@PreDestroy

@PostConstruct 在方法上加上注解@PostConstruct ,这个方法就会在Bean 初始化之后被Spring 容器执行 (注:Bean 初始化包括,实例化Bean ,并装配Bean 的属性(依赖注入))。

@PreDestroy 在方法上加上注解@PreDestroy ,这个方法就会在Bean 被销毁前被Spring 容器执行。

12:@Repository

与@Controller 、@Service 类似,都是向spring 上下文中注册bean。

13:@SessionAttributes

Spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 session 中, 以便下一个请求属对应的 ModelMap 的属性列表中还能访问到这些属性。 这一功能是通过类定义处标注 @SessionAttributes 注解来实现的。 @SessionAttributes 只能声明在类上,而不能声明在方法上。

例如 @SessionAttributes("User") // 将ModelMap 中属性名为User的属性

@SessionAttributes({"attr1","attr2"})

@SessionAttributes(types = User.class)

@SessionAttributes(types = {User.class,Dept.class})

@SessionAttributes(types = {User.class,Dept.class},value={"attr1","attr2"})

14:@InitBinder

如果希望某个属性编辑器仅作用于特定的 Controller ,可以在 Controller 中定义一个标注 @InitBinder 注解的方法, 可以在该方法中向 Controller 了注册若干个属性编辑器

代码语言:javascript
复制
@InitBinder    
public void initBinder(WebDataBinder binder) {    
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false);    
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));    
}  

15:@Required

@ required 负责检查一个bean在初始化时其声明的 set方法是否被执行, 当某个被标注了 @Required 的 Setter 方法没有被调用,则 Spring 在解析的时候会抛出异常,以提醒开发者对相应属性进行设置。 @Required 注解只能标注在 Setter 方法之上。因为依赖注入的本质是检查 Setter 方法是否被调用了,而不是真的去检查属性是否赋值了以及赋了什么样的值。如果将该注解标注在非 setXxxx() 类型的方法则被忽略。

16:@Qualifier

@Autowired @Qualifier("softService")

private ISoftPMService softPMService;

使用@Autowired 时,如果找到多个同一类型的bean,则会抛异常,此时可以使用 @Qualifier("beanName"),明确指定bean的名称进行注入,此时与 @Resource指定name属性作用相同。

17:@PathVariable

@PathVariable是用来获得请求url中的动态参数的,当使用@RequestMapping URI template 样式映射时, 即 someUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解绑定它传过来的值到方法的参数上。

代码语言:javascript
复制
@RequestMapping(value="/user/{userId}",method = RequestMethod.GET)    
public String getUser(@PathVariable("userId") String userId){     
      return "";    
}  

七、动态注入


为什么需要动态注册bean

大部分时候,静态的配置信息即可满足系统需求。但是某些场景下,我们需要根据静态配置中的信息动态生成bean,此时就需要动态注册bean的功能。

如:

用户定义一个如下的接口,而接口的实现则由框架生成,不需要用户自行编写,此时实现类就需要动态注册到容器中。

代码语言:javascript
复制
@Rest("http://localhost:8081/test")
public interface IRequestDemo {

	@GET
	ResultBean get1();

	@GET("/get2")
	ResultBean getWithKey(@Param("key") String key);

	@GET("/get3")
	ResultBean getWithMultKey(@Param("key1") String key,     
	     @Param("key2") String key2);
	
}

@componet
public class test {
  @Autowired
  IRequestDemo demo;

  public String test() {
	String msg = "<h1>invoke remote rest result</h1>";

	ResultBean get1 = demo.get1();

	msg += "<br/>get1 result=" + get1;

	ResultBean get2 = demo.getWithKey("key-------");

	msg += "<br/>get2 result=" + get2;

	ResultBean get3 = demo.getWithMultKey("key11111", "key22222");

	msg += "<br/>get3 result=" + get3;

	return msg;
  }
}

代码片段来自我同事[晓风轻][1]的项目[MyRestUtil][2]

动态注册bean的api

Spring中的bean定义都保存在 **BeanDefinitionRegistry** 接口中,单例的bean的实例都保存在 **SingletonBeanRegistry** 接口中。

因此动态注册bean也分为了两种方式:

1. 使用BeanDefinitionRegistry接口的void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException 方法

2. 使用SingletonBeanRegistry接口的void registerSingleton(String beanName, Object singletonObject) 方法

两者区别在于使用前者时,Spring容器会根据BeanDefinition实例化bean实例,而使用后者时,bean实例就是传递给registerSingleton方法的对象。

DefaultListableBeanFactory接口同时实现了这两个接口,在实践中通常会使用这个接口。

在普通bean中进行动态注册

可以在任何获得了BeanDefinitionRegistry或者SingletonBeanRegistry实例的地方进行动态注册。

但是如果bean不是在BeanFactoryPostProcessor中被注册,那么该bean则无法被**BeanPostProcessor**处理,即无法对其应用aop、Bean Validation等功能。

在**BeanFactoryPostProcessor**中进行动态注册

在Spring容器的启动过程中,BeanFactory载入bean的定义后会立刻执行BeanFactoryPostProcessor,此时动态注册bean,则可以保证动态注册的bean被BeanPostProcessor处理,并且可以保证其的实例化和初始化总是先于依赖它的bean。

例子

在BeanFactoryPostProcessor注册

代码语言:javascript
复制
@Component
@Slf4j
public class PersonBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        DefaultListableBeanFactory defaultListableBeanFactory
                = (DefaultListableBeanFactory) beanFactory;

        //注册Bean定义,容器根据定义返回bean
        log.info("register personManager1>>>>>>>>>>>>>>>>");
        BeanDefinitionBuilder beanDefinitionBuilder =
                BeanDefinitionBuilder.genericBeanDefinition(PersonManager.class);
        beanDefinitionBuilder.addPropertyReference("personDao", "personDao");
        BeanDefinition personManagerBeanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
        defaultListableBeanFactory.registerBeanDefinition("personManager1", personManagerBeanDefinition);

        //注册bean实例
        log.info("register personManager2>>>>>>>>>>>>>>>>");
        PersonDao personDao = beanFactory.getBean(PersonDao.class);
        PersonManager personManager = new PersonManager();
        personManager.setPersonDao(personDao);
        beanFactory.registerSingleton("personManager2", personManager);

    }
}

在普通bean中注册

代码语言:javascript
复制
@RestController
@Slf4j
public class PersonManagerRegisterController {

    /**
     * The Application context.
     */
    @Autowired
    GenericApplicationContext applicationContext;

    /**
     * The Bean factory.
     */
    @Autowired
    ConfigurableBeanFactory beanFactory;

    /**
     * 动态注册bean,此处注册的bean没有AOP的支持
     * curl http://localhost:8080/registerPersonManager
     */
    @GetMapping("/registerPersonManager")
    public void registerPersonManager() {
        PersonDao personDao = applicationContext.getBean(PersonDao.class);
        PersonManager personManager = new PersonManager();
        personManager.setPersonDao(personDao);
        beanFactory.registerSingleton("personManager3", personManager);

    }
... ...
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/03/09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、IOC和依赖注入DI
  • 二、Spring容器
    • 1、Spring BeanFactory 容器:
      • 2、Spring ApplicationContext 容器:
      • 三、Spring Bean配置
      • 四、Spring bean的XML配置
        • 1、import导入
          • 2.设置注入:
            • 2.构造注入:必须写有参的构造函数。
              • 3.依赖注入:当引用了其他类的对象时。
                • 4.集合的注入:
                  • 5.自动注入(spring4):
                  • 五、Spring bean注解配置
                    • 1、注解配置bean的创建
                      • 2、如何找到这些bean的类文件
                        • 3、bean注入
                          • 1 )、构建器注入
                          • 2)、直接通过@Autowried 注解注入
                      • 六、常用Spring注解
                      • 七、动态注入
                        • 为什么需要动态注册bean
                          • 动态注册bean的api
                            • 在普通bean中进行动态注册
                            相关产品与服务
                            容器服务
                            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档