Spring入门基础

发布时间:2026/8/28 15:52:32
Spring入门基础 目录1.Spring的相关概念2.Spring的核心与设计思想3.Spring创建与使用4.更简单地去使用Spring5.Bean的作用域与生命周期* *1.Spring相关概念------------### 1.1 什么是Spring我们通常所说的 Spring 指的是 Spring FrameworkSpring 框架它是个开源框架有着活跃庞 的社区这就是它之所以能久不衰的原因。Spring 持泛的应场景它可以让 Java 企业级的 应程序开发起来更简单。 而Spring的本质其实就是是包含了众多具法的IoC 容器。这里我们有两个关键字一个是IoC另一个则是容器。###1.2 什么是容器容器是来容纳某种物品的基本装置。 比如我们Java中常用的List/Map就是一种数据存储容器 而我们常用于web开发的Tomcat就是一种web容器### 1.3什么是 IoC前面我们提到Spring是一个IoC容器而IoCInversion of Control 翻译成中是“控制反转”的意思。当然光这样理解还是比较抽象的我们下面通过一个例子来理解。2.Spring的核心与设计思想----------------### 2.1 传统程序开发假如我们需要在程序内构建一辆”车“那么我们传统的思想一般是下面这样的。 我构建一辆汽车首先要依赖于车身而车身又依赖于地盘。而底盘需要轮胎。最终我们得到下面的代码。 public class Main { public static void main(String[] args) { Car car new Car(); car.init(); } /* * 汽?对象/ static class Car { public void init() { // 依赖?身 Framework framework new Framework(); framework.init(); } } /* * ?身类/ static class Framework { public void init() { // 依赖底盘 Bottom bottom new Bottom(); bottom.init(); } } /* * 底盘类/ static class Bottom { public void init() { // 依赖轮胎 Tire tire new Tire(); tire.init(); } } /* * 轮胎类/ static class Tire { // 尺? private int size 30; public void init() { System.out.println(“轮胎尺?” size); } } } 虽然这段代码的确能够满足需求但是我们不难发现一个特点就是它的耦合性非常高。假如我们需要更改轮胎的尺寸我们需要将轮胎以及所有依赖轮胎的组件的构造方法全部更改一遍加上size参数例如下面那样。 public class Main { public static void main(String[] args) { Car car new Car(20); car.run(); } /* * 汽?对象/ static class Car { private Framework framework; public Car(int size) { framework new Framework(size); } public void run() { // 依赖?身 framework.init(); } } /* * ?身类/ static class Framework { private Bottom bottom; public Framework(int size) { bottom new Bottom(size); } public void init() { // 依赖底盘 bottom.init(); } } /* * 底盘类/ static class Bottom { private Tire tire; public Bottom(int size) { tire new Tire(size); } public void init() { // 依赖轮胎 tire.init(); } } /* * 轮胎类/ static class Tire { // 尺? private int size; public Tire(int size) { this.size size; } public void init() { System.out.println(“轮胎尺?” size); } } } 而在实际开发过程中需求变更是很常见的。而我们不难看出以上程序的问题是当最底层代码改动之后整个调链上的所有代码都需要修改。这在实际开发过程中就会严重拖慢我们的进度那么有什么办法能够解决这个问题呢。我们看到下面的代码 public class Main { public static void main(String[] args) { Tire tire new Tire(20); Bottom bottom new Bottom(tire); Framework framework new Framework(bottom); Car car new Car(framework); car.run(); } static class Car { private Framework framework; public Car(Framework framework) { this.framework framework; } public void run() { framework.init(); } } static class Framework { private Bottom bottom; public Framework(Bottom bottom) { this.bottom bottom; } public void init() { bottom.init(); } } static class Bottom { private Tire tire; public Bottom(Tire tire) { this.tire tire; } public void init() { tire.init(); } } static class Tire { private int size; public Tire(int size) { this.size size; } public void init() { System.out.println(“轮胎” size); } } } 可以看到我们先创建了所有的下级依赖类然后再通过参数传递的方式注入。这样我们不需 要在当前类中创建下级类了所以下级类即使发变化创建或减少参数当前类本身也需修改任 何代码这样就完成了程序的解耦。以上就是所谓的控制反转式程序开发。此时论底层类如何变化整个调链是不做任何改变的这样就完成了代码之间的解耦从实现了更加灵活、通的程序设计了。### 2.2 简单对比 在传统的代码中对象创建顺序是Car - Framework - Bottom - Tire 改进之后解耦的代码的对象创建顺序是Tire - Bottom - Framework - Car 这里我们可以发现通程序的实现代码类的创建顺序是反的传统代码是 Car 控制并创建了 FrameworkFramework 创建并创建了 Bottom依次往下改进之后的控制权发的反转不再是上级对象创建并控制下级对象了是下级对象把注将当前对象中下级的控制权不再由上级类控制了这样即使下级类发任何改变当前类都是不受影响的这就是典型的控制反转也就是 IoC 的实现思想。### 2.3 理解Spring IoC既然 Spring 是个 IoC控制反转容器重点还在“容器”字上那么它就具备两个最基础的功能将对象存到容器从容器中取出对象。也就是说学 Spring 最核的功能就是学如何将对象存到 Spring 中再从 Spring 中获取对象的过程。 将对象存放到容器中的好处 将对象存储在 IoC 容器相当于将以后可能的所有具制作好都放到仓库中需要的时候直接取就了完再把它放回到仓库。new 对象的式相当于每次需要具了才现做完就扔掉了也不会保存下次再的时候还得重新做这就是 IoC 容器和普通程序开 发的区别。Spring 是个 IoC 容器说的是对象的创建和销毁的权利都交给 Spring 来管理了它本身具备了存储对象和获取对象的能。### 2.4 DI与IoC说到 IoC 不得不提的个词就是“DI”DI 是 Dependency Injection 的缩写翻译成中是*“依赖注 ”的意思。所谓依赖注就是由 IoC 容器在运期间动态地将某种依赖关系注到对象之中。而依赖注DI和控制反转IoC只是从不同的角度的描述的同件事情。 IoC是一种思想而DI是实际的落地执行方案**。IoC是指导DI去执行的3.Spring的创建与使用--------------### 3.1 Spring的创建有关Spring项目的创建我们需要使用到maven类似于我们创建servlet项目。主要分为以下三个步骤1. 创建个普通 Maven 项。2. 添加 Spring 框架持spring-context、spring-beans。3. 添加启动类。* *这里我只讲一些关键的步骤首先我们需要引入Spring框架支持,我们需要在pom.xml文件中引入下面的代码org.springframework spring-context 5.2.3.RELEASE org.springframework spring-beans 5.2.3.RELEASE 之后后在创建好的项 java 件夹下创建个启动类包含 main 法即可 public class App { public static void main(String[] args) { } }### 3.2 Bean对象的存储这里我们需要明确Bean对象其实就是Java中的一个普通对象大家不需要感觉陌生。 存储 Bean 分为以下 2 步 1. 存储 Bean 之前先得有 Bean 才因此先要创建个 Bean。 2. 将创建的 Bean 注册到 Spring 容器中。第一步我们只要任意创建一个对象就好参考下图 package com.beans; public class User { public User() { System.out.println(“加载了 User”); } public void sayHi(String name) { System.out.println(“你好” name); } } * *之后我们需要将Bean注册到Spring容器内具体步骤如下首先在创建好的项中添加 Spring 配置件 spring-config.xml名字可以随便取但建议规范化命名将此件放到 resources 的根录下如 下图所示Spring 配置件的固定格式为以下内容以下内容需记忆只需要保存到可以找到的地就可以 了因为它是固定不变的 ?xml version1.0 encodingUTF-8? 接下来再将 User 对象注册到 Spring 中就可以具体操作是在 中添加如下配置 这里我们可以把String看成一个Map容器。类似于MapString[beanName],Object。 注意id可以不等于class里面的类名但是要尽量符合规范。 类名使用大驼峰bean id通常使用小驼峰。 假设类名是UserInfo那么bean id就应该是 userInfo### 3.3 获取并使用Bean 对象 获取并使 Bean 对象分为以下 3 步 1.得到 Spring 上下对象因为对象都交给 Spring 管理了所以获取对象要从 Spring 中获取那么 就得先得到 Spring 的上下。 2. 通过 Spring 上下获取某个指定的 Bean 对象。 3. 使 Bean 对象。#### 3.3.1创建 Spring 上下文Spring 上下对象可使 ApplicationContext实现代码如下 // 1.得到 Spring 的上下?对象创建的时候需要配置 Spring 配置信息 ApplicationContext context new ClassPathXmlApplicationContext(“spring-config.xml”); 这里尤其需要注意Spring的配置信息不能错很多同学容易写错配置文件名字导致报错比如下面这种错误。除了 ApplicationContext 之外我们还可以使 BeanFactory 来作为 Spring 的上下如下代码所示 BeanFactory beanFactory new XmlBeanFactory(new ClassPathResource(“spring-config.xml”)); ApplicationContext 和 BeanFactory 效果是样的ApplicationContext 属于 BeanFactory 的类** 它们的区别如下。ApplicationContext 与BeanFactory区别常见面试题 1继承关系和功能来说Spring 容器有两个顶级的接BeanFactory 和 ApplicationContext。 其中 BeanFactory 提供了基础的访问容器的能而ApplicationContext 属于 BeanFactory 的子类它除了继承了 BeanFactory 的所有功能之外它还拥有独特的特性还添加了对国际化持、 资源访问持、以及事件传播等的持。 2从性能来说ApplicationContext 是次性加载并初始化所有的 Bean 对象 BeanFactory 是需要那个才去加载那个因此更加轻量。* * PS ClassPathXmlApplicationContext 属于 ApplicationContext 的类拥有 ApplicationContext 的所有功能是通过 xml 的配置来获取所有的 Bean 容器的。#### 3.3.2获取指定的 Bean 对象 // 1.得到 Spring 上下?对象 ApplicationContext context new ClassPathXmlApplicationContext(“spring-config.xml”); // 2.加载某个 bean User user (User) context.getBean(“user”);这里我们需要注意bean的id需要和xml文件内对应上。####3.3.3getBean方法的更多用法1.使用bean name获取bean User user (User) context.getBean(“user”); 2.根据bean type获取bean User user (User) context.getBean(User.class); 这种写法简单一般用于确定该类型只会被注入一次时使用。如果同一类型被注入多次使用该方法则会报错。3.根据bean name和类型获取bean User user (User) context.getBean(“user”,User.class); 4. 更简单地去使用Spring-----------------在之前我们想要将一个对象注入到Spring容器中需要手动在Spring的配置文件中手动添加这明显不太方便我们需要一种更简便的方法来存取bean。### 4.1 配置扫描路径前置工作注意想要将对象成功的存储到 Spring 中我们需要配置下存储对象的扫描包路径只有被配置的 包下的所有类添加了注解才能被正确的识别并保存到 Spring 中。在 spring-config.xml 添加如下配置 ?xml version1.0 encodingUTF-8? content:component-scan base-package“com.beans”/content:component-scan 下面的就是注册扫描的包com.beans 也就是说即使添加了注解如果不是在配置的扫描包下的类对象也是不能被存储到 Spring 中的。 注意这里的扫描路径是包含了其子路径的也就是说如果bean对象是在com.beans的子路径下也是能够被扫描到的### 4.2添加注解存储 Bean 对象上面我们提到为了能够更加方便地存取bean我们需要添加扫描包路径但是仅仅这样是不够的。我们还需要使用注解。 我们先来简单了解一下注解的类型 想要将对象存储在 Spring 中有两种注解类型可以实现 1. 类注解Controller、Service、Repository、Component、Configuration。 2. 法注解Bean。#### 4.2.1 注解的相关使用我们首先看到Controller控制器存储代码如下 Controller// 将对象存储到 Spring 中 public class UserController { public void sayHi(){ System.out.println(“Hello controller”); } } 此时我们先使之前读取对象的式*后续会介绍更简单的来读取上的 UserController 对象如下代码所示 public class Application { public static void main(String[] args) { // 1.得到 spring 上下? ApplicationContext context new ClassPathXmlApplicationContext(“spring-config.xml”); // 2.得到 bean UserController userControllercontext.getBean(“userController”, UserController.class); // 3.调? bean ?法 userController.sayHi(); } }* *其他类注解的用法类似*我就不再过多赘述。大家在使用时只需要更改对应的注解名字即可。这样我们就省去了反复去更改xml配置文件的过程只需要在首次使用时添加扫描包路径以及添加相应的注解就能将bean对象存入Spring容器内了。### 4.3 为什么要有这么多类注解在解决这个问题之前我们先看看五大类注解的源代码。大家通过观察可以发现其实这五大类在功能上并没有本质区别并且其余的四个类注解都是基于Component实现的。既然如此我们为什么要搞出几个只是名字有所不同的类注解呢 这和为什么每个省/市都有的牌号是样的 如陕的牌号就是陕XXXXXXX北京的 牌号京XXXXXXX样。甚个省不同的县区也是不同的如安就是陕AXXXXX咸阳陕BXXXXXX宝鸡陕CXXXXXX样。这样做的好处除了可以节约号码之外更重要的作是可以直观的标识辆的归属地。那么为什么需要怎么多的类注解也是相同的原因就是让程序员看到类注解之后就能直接了解当前类的途如 Controller表示的是业务逻辑层 Servie服务层 Repository持久层 Configuration配置层。 Component可以理解成工具组件独立于其他四种注解我们通过下面这张图来理解配置层就是用于定义配置类可替换xml配置文件 * * 这里的控制层实际上就是负责校验数据的类似于保安。他会过滤掉那些不符合规定的数据。比如我们在处理登录系统时就可以设置一个Controller当识别到数据有误时就可以直接返回ERROR不需要我们反复去写判断逻辑简化了代码。 * * 而服务层负责的就是各种数据组装与接口的调用它并不负责程序执行的实际逻辑更像是一个公司的前台当你去处理业务的时候他会告诉你该去哪调用什么接口去处理。 * * 而数据持久层则是真正的业务逻辑代码它会和数据库进行交互去实现各种接口的逻辑。### 4.4 关于bean对象的命名通过上示例我们可以看出通常我们 bean 使的都是标准的驼峰命名读取的时候字 写就可以获取到 bean 了如下图所示然当我们字和第个字都是写时就不能正常读取到 bean 了如下图所示*这时候我们就需要去查看Spring 关于 bean 存储时成的命名规则了。我们可以以在 Idea 中使搜索关键字“beanName”可以得到以下结果。顺藤摸我们最后找到了 bean 对象的命名规则的法它使的是JDK Introspector 中的 decapitalize 法源码如下 public static String decapitalize(String name) { if (name null || name.length() 0) { return name; } // 如果第?个字?和第?个字?都为?写的情况是把 bean 的?字?也?写存储了 if (name.length() 1 Character.isUpperCase(name.charAt(1)) Character.isUpperCase(name.charAt(0))){ return name; } // 否则就将?字??写 char chars[] name.toCharArray(); chars[0] Character.toLowerCase(chars[0]); return new String(chars); }至此这也就能够解释我们bean对象命名的原理了。### 4.5 方法注解Bean public class Users { Bean public User user1() { User user new User(); user.setId(1); user.setName(“Java”); return user; } }然当我们写完以上代码尝试获取 bean 对象中的 user1 时却发现根本获取不到 public class Application { public static void main(String[] args) { ApplicationContext context new ClassPathXmlApplicationContext(“spring-config.xml”); User usercontext.getBean(“user”, User.class); System.out.println(user.toString()); } }那么为什么直接使用方法注解会报错呢答案是方法注解要配合类注解使用。在 Spring 框架的设计中法注解 Bean 要配合类注解才能将对象正常的存储到 Spring 容器中如 下代码所示 Component public class Users { Bean public User user1() { User user new User(); user.setId(1); user.setName(“Java”); return user; } }#### 4.5.1重命名 Bean可以通过设置 name 属性给 Bean 对象进重命名操作如下代码所示 Component public class Users { Bean(name {“u1”}) public User user1() { User user new User(); user.setId(1); user.setName(“Java”); return user; } } 此时我们使 u1 就可以获取到 User 对象了如下代码所示 class App { public static void main(String[] args) { // 1.得到 spring 上下? ApplicationContext context new ClassPathXmlApplicationContext(“spring-config.xml”); // 2.得到某个 bean User usercontext.getBean(“u1”, User.class); // 3.调? bean ?法 System.out.println(user); } }这个重命名的 name 其实是个数组个 bean 可以有多个名字 Bean(name {“u1”, “us1”}) public User user1() { User user new User(); user.setId(1); user.setName(“Java”); return user; } 此时我们既可以使用u1获取User对象也可以使用us1来获取User对象。### 4.6 获取Bean 对象对象装配获取 bean 对象也叫做对象装配是把对象取出来放到某个类中有时候也叫对象注。 对象装配对象注的实现法以下 3 种 1. 属性注 2. 构造法注 3. Setter 注下我们按照实际开发中的模式将 Service 类注到 Controller 类中。#### 4.6.1 属性注入属性注是使 Autowired 实现的将 Service 类注到 Controller 类中。Service 类实现如下 package com.beans; import org.springframework.stereotype.Service; Service public class UserService { public void sayHi() { System.out.println(“你好Service”); } } Controller 类的实现代码如下 Controller public class UserController2 { //属性注入 Autowired private UserService userService; public void sayHi() { userService.sayHi(); } } 测试代码 public class App { public static void main(String[] args) { ApplicationContext contextnew ClassPathXmlApplicationContext(“spring-config.xml”); UserController2 userController2context.getBean(“userController2”, UserController2.class); userController2.sayHi(); } } 结果如下属性注入的核心如下#### 4.6.2 构造方法注入构造法注是在类的构造法中实现注如下代码所示 public class UserController3 { private UserService userService; Autowired public UserController3(UserService userService){ this.userServiceuserService; } public void sayHi() { userService.sayHi(); } }#### 4.6.3 Setter注入Setter 注和属性的 Setter 法实现类似只不过在设置 set 法的时候需要加上 Autowired 注解如下代码所示 public class UserController4 { private UserService userService; Autowired public void setUserService(UserService userService) { this.userServiceuserService; } }#### 4.6.4 三种注入方式的优缺点分析1.属性注的优点是简洁使用方便缺点是只能于 IoC 容器如果是 IoC 容器不可并且只 有在使的时候才会出现 空指针异常这会导致不能及时发现bug。2.构造法注是Spring 推荐的注式它的缺点是如果有多个注会显得较臃肿但出现这种 情况你应该考虑下当前类是否符合程序的单职责的设计模式了它的优点是通性在使之 前定能把保证注的类不为空。3.Setter 式是 Spring 前期版本推荐的注式但通性不如构造法因为不同语言Setter并不完全一样所有 Spring 现版本已经 推荐使构造法注的式来进类注了。 PS虽然Spring官方推荐使用构造方法注入但实际上Spring很多使用的都是属性注入并且在实际项目中属性注入也是使用的比较多的一种方式#### 4.6.5Resource另种注关键字在进类注时除了可以使 Autowired 关键字之外我们还可以使 Resource 进注如下代码所示 Controller public class UserController2 { //属性注入 Resource private UserService userService; public void sayHi() { userService.sayHi(); } }Resource用法和Autowired基本一致稍有区别我们会在下面提到####4.6.7 Autowired 和 Resource 的区别 出身不同Autowired 来于 Spring Resource 来于 JDK 的注解。 用法不同Autowired支持属性注入构造方法注入和Setter注入。而Resource不支持构造方法注入。 支持的参数不同Resource支持更多的参数设置比如nametype设置。而Autowired只支持required参数设置。### 4.7同类型多个 Bean 报错当出现以下多个 Bean返回同对象类型时程序会报错如下代码所示 Component public class Users { Bean public User user1() { User user new User(); user.setId(1); user.setName(“Java”); return user; } Bean public User user2() { User user new User(); user.setId(2); user.setName(“MySQL”); return user; } } Controller public class UserController4 { // 注? Resource private User user; public User getUser() { return user; } } 运行以上代码就会报错*原因是唯的 Bean 对象。 解决同个类型多个 bean 的解决案有以下两个 使 Resource(name“user1”) 定义。通过重命名来区分不同的对象 使 Qualifier 注解定义名称。此注解类似于过滤器的功能① 使 Resource(name“XXX”) Controller class UserController4 { // 注? Resource(name “user1”) private User user; public User getUser() { return user; } }② 使 Qualifier Controller public class UserController5 { // 注? Autowired Qualifier(value “user2”) private User user; public User getUser() { return user; } }5.bean的作用域与生命周期---------------假设现在有个公共的 Bean提供给 A 户和 B 户使然在使的途中 A 户却“悄悄”地修改 了公共 Bean 的数据导致 B 户在使时发了预期之外的逻辑错误。 我们预期的结果是公共 Bean 可以在各的类中被修改但不能影响到其他类。但实际上公共的bean数据却被修改了。这里我们就不放实例代码了大家只需要知道结果就行。原因分析操作以上问题的原因是因为 Bean 默认情况下是单例状态singleton也就是所有的使的都是同个对象之前我们学单例模式的时候都知道使单例可以很程度上提性能所以在 Spring 中 Bean 的作域默认也是 singleton 单例模式。### 5.1作用域定义限定程序中变量的可范围叫做作域或者说在源代码中定义变量的某个区域就叫做作域。而 Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式比如 singleton 单例作用域就表 示 Bean 在整个 Spring 中只有份它是全局共享的那么当其他修改了这个值之后那么另个人读取到的就是被修改的值。###5.2Bean 的 6 种作用域 Spring 容器在初始化个 Bean 的实例时同时会指定该实例的作域。 Spring有 6 种作域最后四种是基于 Spring MVC 效的 1. singleton单例作域 2. prototype原型作域多例作域 3. request请求作域 4. session回话作域 5. application全局作域 6. websocketHTTP WebSocket 作域 注意后 4 种状态是 Spring MVC 中的值在普通的 Spring 项中只有前两种。#### 5.2.1 singleton描述该作域下的Bean在IoC容器中只存在个实例获取Bean即通过 applicationContext.getBean等法获取及装配Bean即通过Autowired注都是同 个对象。场景通常状态的Bean使该作域。状态表示Bean对象的属性状态不需要更新备注Spring默认选择该作域#### 5.2.2prototype描述每次对该作域下的Bean的请求都会创建新的实例获取Bean即通过 applicationContext.getBean等法获取及装配Bean即通过Autowired注都是新的 对象实例。场景通常有状态的Bean使该作域#### 5.2.3request描述每次http请求会创建新的Bean实例类似于prototype场景次http的请求和响应的共享Bean备注限定SpringMVC中使#### 5.2.4session描述在个http session中定义个Bean实例场景户回话的共享Bean, 如记录个户的登陆信息备注限定SpringMVC中使#### 5.2.5application了解描述在个http servlet Context中定义个Bean实例场景Web应的上下信息如记录个应的共享信息备注限定SpringMVC中使#### 5.2.6 websocket了解描述在个HTTP WebSocket的生命周期中定义个Bean实例场景WebSocket的每次会话中保存了个Map结构的头信息将来包裹客户端消息 头。第次初始化后直到WebSocket结束都是同个Bean。备注限定Spring WebSocket中使#### 5.2.7单例作用域(singleton)和全局作用域(application)区别 singleton 是 Spring Core 的作域application 是 Spring Web 中的作域 singleton 作于 IoC 的容器 application 作于 Servlet 容器。#### 5.2.8 设置 bean的作用域为了解决我们开始提出的那个问题我们可以将bean的作用域由单例作用域singleton设置成原型作用域prototype。这里我们需要使用**Scope注解。** Scope(“prototype”)//直接设置值 Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//使用枚举设置这里我们比较常使用第二种方式因为有idea提示不容易错。其余作用域的设置也是类似的。### 5.3 Bean的生命周期#### 5.3.1 bean的执行流程 启动 Spring 容器 - 实例化 Bean分配内存空间从到有 - Bean 注册到 Spring 中存操作 - 将 Bean 装配到需要的类中取操作#### 5.3.2 bean的生命周期所谓的命周期指的是个对象从诞到销毁的整个命过程我们把这个过程就叫做个对象的命 周期。 Bean 的命周期分为以下 5 部分1.实例化 Bean为 Bean 分配内存空间2.设置属性Bean 注和装配3.Bean 初始化a执行各种通知Awareb执行初始化前置方法c执行构造方法两种执行方式一种是执行PostConstruct优先执行另一种实质性inti-method在xml配置文件中设置的。这两种构造方法实际上是两个时代的产物。d执行初始化后置方法4.使用bean5.销毁beana执行PreDestroyb重写DisposableBean接口方法cdestroy-method#### 5.3.3实例化和初始化的区别实例化和属性设置是 Java 级别的系统“事件”其操作过程不可人工干预和修改初始化是给开发者提供的可以在实例化之后类加载完成之前进定义“事件”处理。#### 5.3.4为什么要先设置属性在进行初始化也就是我们能否将步骤二和步骤三的顺序调换呢我们看到下面一段代码。 Service public class UserService { public UserService(){ System.out.println(“调? User Service 构造?法”); } public void sayHi(){ System.out.println(“User Service SayHi.”); } } Controller public class UserController { Resource private UserService userService; PostConstruct public void postConstruct() { userService.sayHi(); System.out.println(“执? User Controller 构造?法”); } } 对于上述的代码假如我们先执行PostConstruct构造方法再去设置属性Resource注入。那么我们会发现构造方法会用到userService这个属性而假如这个属性还未被注入此时执行构造方法就会引发空指针异常所以要先设置属性在进行初始化。