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提供了以下两种不同类型的容器。
这是最简单的容器DI提供基本的支持和定义由org.springframework.beans.factory.BeanFactory 接口. BeanFactory或者相关的接口,例如实现BeanFactoryAware,InitializingBean,DisposableBean,仍然存在在Spring向后兼容性与大量的与Spring整合第三方框架的目的。
此容器添加了更多的企业特定的功能,例如从一个属性文件解析文本消息的能力,并发布应用程序事件感兴趣的事件监听器的能力。此容器是由 org.springframework.context.ApplicationContext 接口定义.
在ApplicationContext 容器包括BeanFactory的容器的所有功能,所以因此通常建议在BeanFactory。 BeanFactory仍然可以用于重量轻的应用,如移动装置或基于小应用程序的应用中的数据量和速度是显著。
实例化一个spring容器,容器会自动预初始化所有Bean实例。
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); //实例化一个Spring容器,
Boss boss = (Boss) ac.getBean("boss"); //取得对象boss
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容器三个重要的方法:
bean创建的几个重要步骤: 1) 创建bean实例,也就是bean的实体化,创建过程不仅仅只有java的反射机制,还结合了动态代理的方式 2) 记录创建bean的objectFactory 3) 属性注入 4) 初始化bean 5) 注册disposablebean
Spring bean的生命周期:
Spring配置文件是一个或多个标准的XML文档,applicationContext.xml是Spring的默认配置文件,当容器启动时找不到指定的配置文档时,将会尝试加载这个默认的配置文件。
下面列举的是一份比较完整的配置文件模板,文档中各XML标签节点的基本用途也给出了详细的解释
在我们的beans.xml中就可以引入和使用entity.xml文件中的bean配置(代码中config/spring为我们新建的包config.spring)
<import resource="config/spring/entity.xml"/>
Bean的作用域 scope指bean的作用域,在配置bean时,有scope属性来配置bean的作用 注意:在整合struts和spring时,需要将action设为scope="prototype";
<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:在应用范围内,只有一个对象;
-->
属性必须写setter方法。格式为: 要求被注入的属性必须有set方法。set方法的方法名由set+属性(属性首字母大写),如果属性是boolean类型,没有get方法(是is);
public class Student {
private String name;
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
<bean id="student" class="cn.sxt.vo.Student">
<property name="name" value="hgs"></property>
</bean>
public class Test {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("beans.xml");
Student stu=(Student)ac.getBean("student");
stu.show();
}
}
<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,并给它的属性赋值。
<property name=" " ref=" ">或者<constructor-arg ref=" ">
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Student类中引用Address类的对象(Student的实例变量是另外一个类Address的对象):
private Address addr;
public void setAddr(Address addr) {
this.addr = addr;
}
beans.xml:
<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>
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做以下配置
<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>
是用来简化spring的配置文件,在配置bean时,可以配置bean的autowire属性,用于指定注入类型
<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
注解配置相对于 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规范注解方式的处理器:
<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了。
我们先了解@Controller
Spring容器框架包org.springframework.stereotype下Controller注解接口源代码如下
//
// 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容器框架中进行管理。
说完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上下文中的对象。
我们可以自由地使用任何标准的Spring框架技术去定义beans和它们注入的依赖。 简单起见, 我们经常使用 @ComponentScan 注解搜索beans, 并结合 @Autowired 构造器注入。 如果使用上面建议的结构组织代码( 将应用类放到根包下) , 你可以添加 @ComponentScan 注解而不需要任何参数。 你的所有应用程序组件( @Component , @Service , @Repository , @Controller 等) 将被自动注册为Spring Beans。
下面是一个 @Service Bean的示例, 它使用构建器注入获取一个需要的 UserDao bean。
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 后续是不能改变的。
直接通过@Autowried 注解对Service对象进行注解即可
在Controller中: @Controller @RequestMapping("/test") public class ExampleController { @Autowired private ExampleService service; }
在Service中 @Component public class ExampleServiceImpl Implements ExampleService { @Autowired private ExampleDao exampleDao; }
常用注解(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,例如:
@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请求参数。
@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 会自动注入这些实现类的实例,不需要额外的操作。
@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上。
@RequestMapping(value = "/user",method = RequestMethod.GET)
public void UserInfo(@RequestBody String userId,User user){
}
7:@ResponseBody
@ResponseBody与@RequestBody类似,它的作用是将返回类型直接输入到HTTP response body中。最常用的我们使用ajax传输json,需要再类上面配置@ResponseBody。
@ResponseBody
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String list() {
return "Hello World";
}
8:@ModelAttribute @ModelAttribute可以作用在方法或方法参数上,当它作用在方法上时,标明该方法的目的是添加一个或多个模型属性(model attributes)。该方法支持与@RequestMapping一样的参数类型,但并不能直接映射成请求。控制器中的@ModelAttribute方法会在@RequestMapping方法调用之前而调用,示例如下:
@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 了注册若干个属性编辑器
@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注解绑定它传过来的值到方法的参数上。
@RequestMapping(value="/user/{userId}",method = RequestMethod.GET)
public String getUser(@PathVariable("userId") String userId){
return "";
}
大部分时候,静态的配置信息即可满足系统需求。但是某些场景下,我们需要根据静态配置中的信息动态生成bean,此时就需要动态注册bean的功能。
如:
用户定义一个如下的接口,而接口的实现则由框架生成,不需要用户自行编写,此时实现类就需要动态注册到容器中。
@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]
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接口同时实现了这两个接口,在实践中通常会使用这个接口。
可以在任何获得了BeanDefinitionRegistry或者SingletonBeanRegistry实例的地方进行动态注册。
但是如果bean不是在BeanFactoryPostProcessor中被注册,那么该bean则无法被**BeanPostProcessor**处理,即无法对其应用aop、Bean Validation等功能。
在**BeanFactoryPostProcessor**中进行动态注册
在Spring容器的启动过程中,BeanFactory载入bean的定义后会立刻执行BeanFactoryPostProcessor,此时动态注册bean,则可以保证动态注册的bean被BeanPostProcessor处理,并且可以保证其的实例化和初始化总是先于依赖它的bean。
例子
在BeanFactoryPostProcessor注册
@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中注册
@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);
}
... ...