网站怎么做搜索引擎优化,域名自助服务平台,网站做不做百度云加速,住建蓉e办官网依赖注入和依赖查找 应该说IoC的工作方式有两种#xff0c;一种是依赖查找#xff0c;通过资源定位#xff0c;把对应的资源查找出来#xff0c;例如通过JNDI找到数据源#xff0c;依赖查找被广泛使用在第三方的资源注入上#xff0c;比如在Web项目中#xff0c;数据源往…依赖注入和依赖查找 应该说IoC的工作方式有两种一种是依赖查找通过资源定位把对应的资源查找出来例如通过JNDI找到数据源依赖查找被广泛使用在第三方的资源注入上比如在Web项目中数据源往往是通过服务器配置的例如Tomcat的数据源配置这种场景下便可以用JNDI的形式通过接口将其注入Spring IoC容器另一种则是依赖注入其主要是在容器内通过类型或者名称查找资源来管理Bean之间的依赖关系而就依赖注入而言又可分为构造器注入和setter注入两种方式setter的形式是Spring推荐的也是应用更广泛的形式 构造器注入
构造器注入依赖于构造方法的实现而构造方法可以是有参数的或者是无参数的在大部分情况下我们通过类的构造方法创建类对象Spring也可以采用反射的方式通过使用构造方法完成注入这便是构造器注入的原理
要让Spring更好的完成对应的构造注入有必要描述具体的类、构造方法、设置对应的参数如此Spring就会通过对应的信息用反射的形式创建对象
如下代码所示首先定义一个新的Role pojo
package com.ssm.pojo;
/*** 角色类用于表示角色信息*/
public class RoleII {private Long id; // 角色编号private String roleName; // 角色名称private String note; // 备注信息/*** 无参数构造方法用于创建一个空的角色对象。*/public RoleII() {}/*** 带参数的构造方法用于创建一个具有指定角色信息的角色对象。** param id 角色的唯一标识符。* param roleName 角色的名称。* param note 对角色的备注说明。*/public RoleII(Long id, String roleName, String note) {this.id id;this.roleName roleName;this.note note;}// 提供对id属性的访问和修改public Long getId() {return id;}public void setId(Long id) {this.id id;}// 提供对roleName属性的访问和修改public String getRoleName() {return roleName;}public void setRoleName(String roleName) {this.roleName roleName;}// 提供对note属性的访问和修改public String getNote() {return note;}public void setNote(String note) {this.note note;}
}要使用带参数的构方法创建对象需要在配置文件spring-cfg.xml中进行适当的配置如下所示 !-- 定义一个RoleII类型的bean通过构造器进行初始化 --bean idrole1 classcom.ssm.pojo.RoleII!-- 构造器参数1角色ID这里设置为1 --constructor-arg index0 value1 /!-- 构造器参数2角色名称这里设置为总经理 --constructor-arg index1 value总经理 /!-- 构造器参数3角色描述这里设置为公司管理者 --constructor-arg index2 value公司管理者 //bean其中constructor-arg元素用于定义构造方法的参数index用于定位参数的位置从0开始通过这样的定义Spring便会使用RoleII(Long id, String roleName, String note)方法创建对象 参数少的时候还好参数多了这种构造形式可读性会比较差 setter注入
setter注入是Spring中最主流的注入方式它利用JavaBean规范定义的setter方法完成注入不但灵活而且可读性高消除了使用构造器注入时出现多个参数可读性变差的问题在RoleII的pojo里有个无参数的构造函数通过配置Spring也可以通过Java反射技术注入 !-- 定义一个RoleII类型的bean实例id为2角色名为高级工程师备注为重要人员 --bean idrole2 classcom.ssm.pojo.RoleII scopeprototype!-- 设置角色id为2 --property nameid value2 /!-- 设置角色名为高级工程师 --property nameroleName value高级工程师 /!-- 设置备注为重要人员 --property namenote value重要人员 //beanscope属性定义了Spring容器管理的Bean的实例化策略即Bean的作用域。不同的作用域会影响Bean的创建和生命周期管理。以下是Spring支持的主要作用域 Singleton (单例): 这是默认的作用域。当一个Bean被配置为单例时Spring容器只会创建该Bean的一个实例并在第一次请求时初始化。之后的每次请求都将返回同一个实例。适合那些在整个应用中需要共享同一状态的对象。Prototype (原型): 原型作用域表示每次请求都会创建一个新的Bean实例。这适用于那些需要有各自独立状态的对象比如用户会话或者并发操作中的对象。Request: 在Web应用中每个HTTP请求都会创建一个新的Bean实例。这意味着对于每个请求都会有一份独立的Bean副本。Session: 也是在Web应用中每个HTTP session会创建一个新的Bean实例。这意味着每个用户session都有自己的Bean副本可以用于存储用户特定的信息。Application: 在Web应用中这个作用域的Bean在整个ServletContext应用上下文中是唯一的类似于单例但作用于整个Web应用而不是容器本身。Global Session: 在Portlet应用中全局session作用域的Bean在一个全局portlet session中是唯一的。这在多portlet环境中用于跨portlet共享状态 选择正确的scope有助于优化应用性能并确保正确管理对象的状态 这样Spring就会通过反射调用没有参数的构造方法生成对象同时通过发射对应的setter方法注入配置的值 property 和 constructor-arg 是Spring框架中用于XML配置文件中进行依赖注入Dependency Injection, DI的两个不同方式。 constructor-arg 用于通过构造函数来注入依赖。它指定调用构造函数时传递给构造函数的参数。当类有无参构造函数或有参构造函数时可以使用此标签来指定哪个构造函数应该被调用并提供对应的参数值。参数可以通过value、ref、index等属性来设置value用于直接注入基本类型或字符串ref用于引用其他beanindex或name用于指定构造函数参数的位置或名称如果构造函数有多个相同类型的参数。property 用于通过setter方法来注入依赖。它对应于Java对象的属性即调用setter方法来设置对象的属性值。这种方式适用于类中有getter/setter方法的属性。和constructor-arg一样property也可以通过value或ref来设置值。 主要区别 注入方式constructor-arg是构造函数注入而property是setter方法注入。初始化顺序构造函数注入通常在对象实例化时发生而setter注入可以在对象创建后任何时候进行。强制性构造函数注入可能是强制性的如果类只有一个无参构造函数那么必须使用setter注入如果有带参数的构造函数Spring会根据提供的constructor-arg调用相应的构造函数。安全性构造函数注入通常被认为更安全因为它保证了对象在创建时就处于一致和完整状态而setter注入的对象可能在初始化后才被完全设置好 依赖查找
有些资源并非来自系统例如数据库连接资源完全可以在Tomcat下配置然后通过JNDI形式获取这种数据库连接资源属于工程外资源就可以采用接口注入的形式获取它
顺便讲一下Tomcat配置JNDIJava Naming and Directory Interface主要涉及在Tomcat的配置文件中定义资源和资源引用
方式1在context.xml中配置找到conf/context.xml文件添加以下元素来定义JNDI资源例如一个数据源
Resource namejndi/ssm authContainertypejavax.sql.DataSourcedriverClassNamecom.mysql.jdbc.Driverurljdbc:mysql://localhost:3306/ssm?useUnicodetrue;characterEncodingutf8usernameroot passwordxxxxmaxActive20 maxIdle10 maxWait10000/方式2在server.xml中配置全局资源打开conf/server.xml文件在元素内添加元素这样配置的资源可以被所有应用共享
GlobalNamingResources...Resource namejndi/globalSsm authContainertypejavax.sql.DataSourcedriverClassNameoracle.jdbc.driver.OracleDriverurljdbc:oracle:thin:127.0.0.1:1521:ORCLusernamescott passwordtigermaxActive20 maxIdle10 maxWait10000/...
/GlobalNamingResources
然后在每个应用的context.xml文件中通过元素引用全局资源, 如下所示
ResourceLink namejndi/localDemo globaljndi/globalSsm typejavax.sql.DataSource/也可以在应用的WEB-INF/web.xml文件中添加元素来声明应用将要使用的资源完成资源引用
resource-refdescriptionDB Connection/descriptionres-ref-namejndi/localDemo/res-ref-nameres-typejavax.sql.DataSource/res-typeres-authContainer/res-auth
/resource-ref一旦在全局或某个应用的context.xml中定义了资源接下来需要在具体的应用上下文中引用这些资源。这通常通过在应用的WEB-INF/web.xml文件中添加元素来完成或者在应用的特定context.xml如果有的话使用来链接到全局资源 返回来假设在Tomcat的context.xml文件中有如下JNDI配置
Resource namejndi/ssm authContainertypejavax.sql.DataSourcedriverClassNamecom.mysql.cj.jdbc.Driverurljdbc:mysql://localhost:3306/ssm?useUnicodetrue;characterEncodingutf8usernameroot passwordxxxxmaxActive20 maxIdle10 maxWait10000/如果Tomcat的Web项目使用了Spring那么可以通过Spring的机制用JNDI获取Tomcat启动的数据库连接池在webapp---WEB-INF---applicationContext.xml文件中加入配置
!-- 定义一个数据源bean通过JNDI技术从应用程序服务器中获取数据源 --
bean iddataSource classorg.springframework.jndi.JndiObjectFactoryBean!-- 设置JNDI名称用于在Java EE服务器中查找对应的DataSource --property namejndiNamevaluejava:comp/env/jdbc/ssm/value/property
/bean这样就可以在Spring的IoC容器中获得Tomcat管理的数据库连接池了这就是一种接口注入的方式
装配Bean
Spring提供了3种配置方式在XML中显示配置、在Java的接口和类中实现配置、隐式Bean的发现机制和自动装配原则在实际工作中这三种方式都会被用到混合使用
基于约定优于配置的原则最优先的应该是通过隐式Bean的发现机制和基于自动装配的原则这样的好处是减小程序开发者的决定权简单又不失灵活在没有办法使用自动装配原则的情况下应该优先考虑在Java接口和类中实现配置这样的好处是减少XML配置的泛滥例如一个父类有多个子类通过IoC容器初始化一个父类容器将无法知道使用哪个子类初始化这个时候使用Java的注解配置指定在上述方法都无法使用的情况下可以使用XML进行配置也可以使用Java配置文件的new关键字创建对象配置例如在实际工作中常常会用到第三方类库常常无法修改里面的代码这个时候可以通过这样的方式配置
如果配置的类是开发者自身正在开发的项目那么应该考虑以Java配置为主而Java配置又分为自动装配和Bean名称配置优先使用自动装配可以减少大量的XML配置如果所需配置的类是第三方的建议使用XML或者Java配置文件的方式
通过XML配置装备Bean
在使用XML装配Bean需要定义对应的XML这里需要引入对应的XML模式(XSD)文件
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd!-- 定义一个BeanPostProcessor的实现类用于bean创建后的处理 --bean idbeanPostProcessor classcom.ssm.bean.BeanPostProcessorImpl /!-- 定义一个DisposableBean的实现类用于bean销毁前的处理 --bean iddisposableBean classcom.ssm.bean.DisposableBeanImpl /!-- 配置一个具有属性的bean表示豆浆的原材料配置 --bean idsource classcom.ssm.pojo.Sourceproperty namebean value黑豆 /property namesugar value少糖 /property namesize value大杯 //bean!-- 配置一个延迟初始化的bean表示豆浆机II使用了初始化和销毁方法 --bean idsoybeanMilkMakerII classcom.ssm.pojo.SoybeanMilkMakerII lazy-inittrue init-methodinit destroy-methoddestroyproperty namebeverageShop value贡茶 /property namesource refsource //bean!-- 定义一个RoleII类型的bean通过构造器进行初始化 --bean idrole1 classcom.ssm.pojo.RoleII!-- 构造器参数1角色ID这里设置为1 --constructor-arg index0 value1 /!-- 构造器参数2角色名称这里设置为总经理 --constructor-arg index1 value总经理 /!-- 构造器参数3角色描述这里设置为公司管理者 --constructor-arg index2 value公司管理者 //bean!-- 定义一个RoleII类型的bean实例id为2角色名为高级工程师备注为重要人员 --bean idrole2 classcom.ssm.pojo.RoleII scopeprototype!-- 设置角色id为2 --property nameid value2 /!-- 设置角色名为高级工程师 --property nameroleName value高级工程师 /!-- 设置备注为重要人员 --property namenote value重要人员 //bean
/beans在文件的头部引入了一个元素的定义它是一个根元素同时XSD文件也被引入使用该文件所定义的元素可以定义对应的Spring Bean
装配简单值 !-- 定义一个RoleII类型的bean实例id为2角色名为高级工程师备注为重要人员 --bean idrole2 classcom.ssm.pojo.RoleII!-- 设置角色id为2 --property nameid value2 /!-- 设置角色名为高级工程师 --property nameroleName value高级工程师 /!-- 设置备注为重要人员 --property namenote value重要人员 //bean对应pojo里实体类的构造函数就很清楚配置项的涵义 /*** 带参数的构造方法用于创建一个具有指定角色信息的角色对象。** param id 角色的唯一标识符。* param roleName 角色的名称。* param note 对角色的备注说明。*/public RoleII(Long id, String roleName, String note) {this.id id;this.roleName roleName;this.note note;}id,它是Spring找bean的时候会找它的编号也就是这个id但id不是一个必须的属性如果没有声明它Spring将采用全限定名#{number}的格式生成编号例如上边这个例子如果没有idrole2那么Spring将会为这个bean生成一个编号com.ssm.pojo.RoleII#0那么下一个没有声明id的bean就是com.ssm.pojo.RoleII#1很显然自动生成id并没有自己定义更清晰class是pojo类的全限定名 这样定义很简单稍微复杂一点的例如在bean的配置中需要注入一些自定义的类 !-- 配置一个具有属性的bean表示豆浆的原材料配置 --bean idsource classcom.ssm.pojo.Sourceproperty namebean value黑豆 /property namesugar value少糖 /property namesize value大杯 //bean!-- 配置一个延迟初始化的bean表示豆浆机II使用了初始化和销毁方法 --bean idsoybeanMilkMakerII classcom.ssm.pojo.SoybeanMilkMakerII lazy-inittrue init-methodinit destroy-methoddestroyproperty namebeverageShop value贡茶 /property namesource refsource //bean如代码所示首先定义了一个id为source的bean然后再通过ref被id为soybeanMilkMakerII的bean引入
看一下对应的Pojo类
package com.ssm.pojo;
/*** Source类用于表示饮品的来源信息。*/
public class Source {private String bean; // 饮品的类型private String sugar; // 饮品的糖分描述private String size; // 饮品的大小杯/*** 无参构造方法用于创建一个新的Source对象。*/public Source() {System.out.println(构造方法.....);}public Source(String bean, String sugar, String size) {this.bean bean;this.sugar sugar;this.size size;}/*** 获取饮品类型。** return 返回当前饮品的类型。*/public String getBean() {return bean;}/*** 设置饮品类型。** param bean 需要设置的饮品类型。*/public void setBean(String bean) {this.bean bean;}/*** 获取饮品的糖分描述。** return 返回当前饮品的糖分描述。*/public String getSugar() {return sugar;}/*** 设置饮品的糖分描述。** param sugar 需要设置的饮品糖分描述。*/public void setSugar(String sugar) {this.sugar sugar;}/*** 获取饮品的大小杯。** return 返回当前饮品的大小杯。*/public String getSize() {return size;}/*** 设置饮品的大小杯。** param size 需要设置的饮品大小杯。*/public void setSize(String size) {this.size size;}
}
package com.ssm.pojo;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;/*** SoybeanMilkMakerII类实现了多种Spring接口包括BeanNameAware, BeanFactoryAware,* ApplicationContextAware, InitializingBean用于展示如何与Spring容器交互。* 这个类模拟了一个豆奶制作机能够制作豆奶并提供自定义初始化和销毁逻辑。*/
public class SoybeanMilkMakerII implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean{private String beverageShop null; // 饮品店品牌private Source source null; // 果汁原料描述// Getter 方法public String getBeverageShop() {return beverageShop;}// Setter 方法确保参数类型与getter返回类型匹配public void setBeverageShop(String beverageShop) {this.beverageShop beverageShop;}//Getter methodpublic Source getSource() {return this.source;}// Setter methodpublic void setSource(Source source) {this.source source;}/*** SoybeanMilkMakerII的构造方法打印构造信息。*/public SoybeanMilkMakerII() {System.out.println(SoybeanMilkMakerII的构造方法);}/*** 制作豆奶的方法。* return 返回一杯具有特定品牌、大小、糖分和水果的豆奶描述。*/public String makeSoybeanMilk() {String soybeanMilk 这是一杯由 beverageShop 饮品店提供的 source.getSize() source.getSugar() source.getBean();return soybeanMilk;}/*** 自定义初始化方法展示如何在Bean初始化后执行特定逻辑。*/public void init() {System.out.println(【 this.getClass().getSimpleName() 】执行自定义初始化方法);}/*** 自定义销毁方法展示如何在Bean销毁前执行特定逻辑。*/public void destroy() {System.out.println(【 this.getClass().getSimpleName() 】执行自定义销毁方法);}/*** 实现BeanNameAware接口的方法用于获取Bean的名称。* param beanName Bean的名称。*/Overridepublic void setBeanName(String beanName) {System.out.println(【 this.getClass().getSimpleName() 】调用BeanNameAware接口的setBeanName方法);}/*** 实现BeanFactoryAware接口的方法用于获取BeanFactory。* param bf BeanFactory实例。* throws BeansException 如果设置过程中发生错误。*/Overridepublic void setBeanFactory(BeanFactory bf) throws BeansException {System.out.println(【 this.getClass().getSimpleName() 】调用BeanFactoryAware接口的setBeanFactory方法);}/*** 实现ApplicationContextAware接口的方法用于获取ApplicationContext。* param ctx ApplicationContext实例。* throws BeansException 如果设置过程中发生错误。*/Overridepublic void setApplicationContext(ApplicationContext ctx) throws BeansException {System.out.println(【 this.getClass().getSimpleName() 】调用ApplicationContextAware接口的setApplicationContext方法);}/*** 实现InitializingBean接口的方法用于在所有属性设置完成后执行初始化操作。* throws Exception 如果初始化过程中发生错误。*/Overridepublic void afterPropertiesSet() throws Exception {System.out.println(【 this.getClass().getSimpleName() 】调用InitializingBean接口的afterPropertiesSet方法);}}装配集合
有些时候要做复杂的装配工作比如Set、Map、List、Array和Properties等定义如下pojo
package com.ssm.pojo;import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/*** ComplexAssembly类用于演示装配复杂数据类型。* 该类包含了一系列属性分别是id、list、map、props、set和array。* 这些属性通过对应的setter和getter方法进行访问和设置。*/
public class ComplexAssembly {private Long id; // 唯一标识private ListString list; // 字符串列表private MapString, String map; // 字符串键值对映射private Properties props; // 属性集合常用于配置信息存储private SetString set; // 不含重复元素的集合private String[] array; // 字符串数组/*** 获取id属性。* return 返回当前对象的唯一标识。*/public Long getId() {return id;}/*** 设置id属性。* param id 欲设置的唯一标识。*/public void setId(Long id) {this.id id;}/*** 获取list属性。* return 返回当前对象的字符串列表。*/public ListString getList() {return list;}/*** 设置list属性。* param list 欲设置的字符串列表。*/public void setList(ListString list) {this.list list;}/*** 获取map属性。* return 返回当前对象的字符串键值对映射。*/public MapString, String getMap() {return map;}/*** 设置map属性。* param map 欲设置的字符串键值对映射。*/public void setMap(MapString, String map) {this.map map;}/*** 获取props属性。* return 返回当前对象的属性集合。*/public Properties getProps() {return props;}/*** 设置props属性。* param props 欲设置的属性集合。*/public void setProps(Properties props) {this.props props;}/*** 获取set属性。* return 返回当前对象的不含重复元素的集合。*/public SetString getSet() {return set;}/*** 设置set属性。* param set 欲设置的不含重复元素的集合。*/public void setSet(SetString set) {this.set set;}/*** 获取array属性。* return 返回当前对象的字符串数组。*/public String[] getArray() {return array;}/*** 设置array属性。* param array 欲设置的字符串数组。*/public void setArray(String[] array) {this.array array;}}要装配这个pojo类其xml配置如下
!-- 定义一个复杂组件装配的bean --
bean idcomplexAssemblyclasscom.ssm.pojo.ComplexAssembly!-- 设置组件的id为1 --property nameid value1 /!-- 设置一个字符串列表包含多个值 --property namelistlistvaluevalue-list-1/valuevaluevalue-list-2/valuevaluevalue-list-3/value/list/property!-- 设置一个键值对映射包含多个键值对 --property namemapmapentry keykey1 valuevalue-map-1 /entry keykey2 valuevalue-map-2 /entry keykey3 valuevalue-map-3 //map/property!-- 设置一个属性集合每个属性包含键和值 --property namepropspropsprop keyprop1value-prop-1/propprop keyprop2value-prop-2/propprop keyprop3value-prop-3/prop/props/property!-- 设置一个无序的值集合 --property namesetsetvaluevalue-set-1/valuevaluevalue-set-2/valuevaluevalue-set-3/value/set/property!-- 设置一个值数组 --property namearrayarrayvaluevalue-array-1/valuevaluevalue-array-2/valuevaluevalue-array-3/value/array/property
/bean上面代码是对字符串的各个集合的装载有些时候可能需要更复杂的装载例如一个List可以是一个系列类的对象又如一个Map集合类键是一个类对象值也是一个类对象
package com.ssm.pojo;public class RoleIII {private Long id; // 角色编号private String roleName; // 角色名称private String note; // 备注信息/*** 无参数构造方法用于创建一个空的角色对象。*/public RoleIII() {}/*** 带参数的构造方法用于创建一个具有指定属性的角色对象。* param id 角色的唯一标识符。* param roleName 角色的名称。* param note 关于角色的备注信息。*/public RoleIII(Long id, String roleName, String note) {this.id id;this.roleName roleName;this.note note;}/**** setters and getters ****/// 获取角色的IDpublic Long getId() {return id;}// 设置角色的IDpublic void setId(Long id) {this.id id;}// 获取角色的名称public String getRoleName() {return roleName;}// 设置角色的名称public void setRoleName(String roleName) {this.roleName roleName;}// 获取角色的备注信息public String getNote() {return note;}// 设置角色的备注信息public void setNote(String note) {this.note note;}
}package com.ssm.pojo;/*** User类用于表示用户信息。*/
public class User {private Long id; // 用户的唯一标识private String userName; // 用户名private String note; // 用户备注信息/*** 获取用户的唯一标识。* return 用户的ID。*/public Long getId() {return id;}/*** 设置用户的唯一标识。* param id 要设置的用户ID。*/public void setId(Long id) {this.id id;}/*** 获取用户的用户名。* return 用户的用户名。*/public String getUserName() {return userName;}/*** 设置用户的用户名。* param userName 要设置的用户名。*/public void setUserName(String userName) {this.userName userName;}/*** 获取用户的备注信息。* return 用户的备注信息。*/public String getNote() {return note;}/*** 设置用户的备注信息。* param note 要设置的用户备注信息。*/public void setNote(String note) {this.note note;}}
package com.ssm.pojo;
import java.util.List;
import java.util.Map;
import java.util.Set;
/*** UserRoleAssembly 类用于组装和管理用户角色关系。* 它包含了对用户角色关系的不同类型集合的管理如列表、映射和集合。*/
public class UserRoleAssembly {private Long id; // 唯一标识符private ListRoleIII list; // 角色列表private MapRoleIII, User map; // 角色到用户的映射private SetRoleIII set; // 角色集合/**** setters and getters ****//*** 获取UserRoleAssembly的唯一标识符。* return 返回此实例的唯一标识符。*/public Long getId() {return id;}/*** 设置UserRoleAssembly的唯一标识符。* param id 要设置的唯一标识符。*/public void setId(Long id) {this.id id;}/*** 获取角色列表。* return 返回此实例的角色列表。*/public ListRoleIII getList() {return list;}/*** 设置角色列表。* param list 要设置的角色列表。*/public void setList(ListRoleIII list) {this.list list;}/*** 获取角色到用户的映射。* return 返回此实例的角色到用户映射。*/public MapRoleIII, User getMap() {return map;}/*** 设置角色到用户的映射。* param map 要设置的角色到用户映射。*/public void setMap(MapRoleIII, User map) {this.map map;}/*** 获取角色集合。* return 返回此实例的角色集合。*/public SetRoleIII getSet() {return set;}/*** 设置角色集合。* param set 要设置的角色集合。*/public void setSet(SetRoleIII set) {this.set set;}}
则XML需要如下配置
!-- 定义Role实体 Bean --
bean idrole_1 classcom.ssm.pojo.RoleIIIproperty nameid value1 /property nameroleName valuerole_name_1 /property namenote valuerole_note_1 /
/beanbean idrole_2 classcom.ssm.pojo.RoleIIIproperty nameid value2 /property nameroleName valuerole_name_2 /property namenote valuerole_note_2 /
/bean!-- 定义User实体 Bean --
bean iduser_1 classcom.ssm.pojo.Userproperty nameid value1 /property nameuserName valueuser_name_1 /property namenote valuerole_note_1 /
/beanbean iduser_2 classcom.ssm.pojo.Userproperty nameid value2 /property nameuserName valueuser_name_2 /property namenote valuerole_note_1 /
/bean!-- 定义UserRoleAssembly实体 Bean用于组装用户和角色的关系 --
bean iduserRoleAssemblyclasscom.ssm.pojo.UserRoleAssemblyproperty nameid value1 /property namelistlistref beanrole_1 /ref beanrole_2 //list/propertyproperty namemapmapentry key-refrole_1 value-refuser_1 /entry key-refrole_2 value-refuser_2 //map/propertyproperty namesetsetref beanrole_1 /ref beanrole_2 //set/property
/bean命名空间装配
Spring还提供了对应的命名空间的定义在使用命名空间的时候要先引入对应的命名空间和XML模式(XSD)文件, 如下代码所示
?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:chttp://www.springframework.org/schema/cxmlns:phttp://www.springframework.org/schema/pxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdxmlns:chttp://www.springframework.org/schema/cxmlns:phttp://www.springframework.org/schema/p这两行定义了XML的命名空间这样才能在内容里面使用p和c这样的前缀如下代码所示 !-- 使用构造方法注入创建Role对象 --bean idc_role classcom.ssm.pojo.RoleIII c:_08c:_1role_name_c c:_2role_note_c /!-- 使用setter注入创建Role对象 --bean idp_role classcom.ssm.pojo.RoleIII p:id9p:roleNamerole_name_p p:noterole_note_p /在这段XML配置中我们通过标签创建了两个Role对象。这里涉及到了两种不同的属性注入方式C风格和P风格。 C风格的属性注入 c:_0“8” 表示注入第一个属性根据Role类的定义这可能是id属性。c:_1“role_name_c” 表示注入第二个属性可能是roleName属性。c:_2“role_note_c” 表示注入第三个属性可能是note属性。 P风格的属性注入 p:id“9” 明确指出了注入的属性名为id其值为9。p:roleName“role_name_p” 明确指出了注入的属性名为roleName其值为role_name_p。p:note“role_note_p” 明确指出了注入的属性名为note其值为role_note_p。 这种方式在配置时更加直观可以根据属性名直接确定注入的值便于理解和维护 如下是通过命名空间定义UserRoleAssembly类实例
?xml version1.0 encodingUTF-8 ?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:chttp://www.springframework.org/schema/cxmlns:phttp://www.springframework.org/schema/pxmlns:utilhttp://www.springframework.org/schema/utilxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsd!-- 使用构造方法注入创建RoleIII对象通过构造函数参数初始化对象 --bean idc_role classcom.ssm.pojo.RoleIIIc:_08 c:_1role_name_c c:_2role_note_c /!-- 使用setter方法注入创建RoleIII对象通过调用setter方法初始化对象属性 --bean idp_role classcom.ssm.pojo.RoleIIIp:id9 p:roleNamerole_name_p p:noterole_note_p /!-- 装配多个RoleIII和User对象示例演示了通过构造方法和setter方法的混合注入 --bean idrole1 classcom.ssm.pojo.RoleIIIc:_01 c:_1role_name_1 c:_2role_note_1 /bean idrole2 classcom.ssm.pojo.RoleIIIp:id2 p:roleNamerole_name_2 p:noterole_note_2 /bean iduser1 classcom.ssm.pojo.Userp:id1 p:userNamerole_name_1 p:noteuser_note_1 /bean iduser2 classcom.ssm.pojo.Userp:id2 p:userNamerole_name_2 p:noteuser_note_2 /!-- 装配一个List对象包含已定义的role1和role2两个Bean --util:list idlistref beanrole1 /ref beanrole2 //util:list!-- 装配一个Map对象将role和user Bean以键值对形式装配 --util:map idmapentry key-refrole1 value-refuser1 /entry key-refrole2 value-refuser2 //util:map!-- 装配一个Set对象包含已定义的role1和role2两个Bean --util:set idsetref beanrole1 /ref beanrole2 //util:set!-- 创建UserRoleAssembly对象并将其与之前定义的List、Map、Set对象关联 --bean iduserRoleAssemblyclasscom.ssm.pojo.UserRoleAssemblyp:id1 p:list-reflist p:map-refmap p:set-refset //beans通过注解装配Bean
实际上并不推荐使用XML的形式装配Bean避免XML文件泛滥使用注解的方式可以减少XML注解的方式既能够实现XML的功能也能够提供自动装配的功能采用自动装配后开发者所需做的决策就少了更有利于程序的开发这便是约定优于配置的理念
Spring提供了两种方式让Spring IoC容器发现Bean大部分的项目都可以用Java配置完成而不是XML这样可以有效减少配置并避免引入大量XML解决了在Sping3之前的版本需要配置大量XML的问题
组件扫描通过定义资源的方式让SpringIoC容器扫描对应的包从而把Bean装配起来自动装配通过注解定义使一些依赖关系可以通过注解完成 使用注解为主XML为辅的方式更加合理比如当系统存在多个公共的配置文件时候(properties和xml)如果都写到注解里显然代码里就分散了各种公共配置不利于管理这个时候XML的配置就更合理一些又或者一些类来自第三方而不是系统开发的配置文件这时利用XML的方式会更加明确 使用注解Component装配Bean
package com.ssm.pojo;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** RoleIV类表示一个角色实体它包含了角色的基本信息。* 通过Spring的Component注解将其标记为一个Bean名称为role。*/
Component(value role)
public class RoleIV {// 通过Value注解注入角色ID这里硬编码为1。Value(1)private Long id;// 通过Value注解注入角色名称这里硬编码为admin。Value(admin)private String roleName;// 通过Value注解注入角色备注信息这里硬编码为administrator。Value(administrator)private String note;/*** 无参构造函数打印构造函数名称。*/public RoleIV(){System.out.println(RoleIV());}/*** 带参数的构造函数用于初始化角色信息。* * param id 角色ID* param roleName 角色名称* param note 角色备注信息*/public RoleIV(Long id, String roleName, String note){System.out.println(RoleIV(Long id, String roleName, String note));this.id id;this.roleName roleName;this.note note;}/*** 获取角色名称。* * return 角色名称*/public String getRoleName(){return roleName;}/*** 设置角色名称。* * param roleName 要设置的角色名称*/public void setRoleName(String roleName){this.roleName roleName;}/*** 获取角色备注信息。* * return 角色备注信息*/public String getNote(){return note;}/*** 设置角色备注信息。* * param note 要设置的角色备注信息*/public void setNote(String note){this.note note;}/*** 获取角色ID。* * return 角色ID*/public Long getId(){return id;}/*** 设置角色ID。* * param id 要设置的角色ID*/public void setId(Long id){this.id id;}/*** 重写toString方法便于打印角色信息。* * return 描述角色信息的字符串*/Overridepublic String toString(){return RoleIV [id id , roleName roleName , note note ];}
} Component 是 Spring 框架中的一个注解它是 Spring 的核心组件之一用于标记一个 Java 类作为 Spring 容器管理的 Bean。当 Spring 容器启动时它会扫描带有 Component 注解的类并将这些类实例化为 Bean然后存储在 Spring 容器中以便在需要时可以自动注入到其他依赖于它们的类中。 在代码中Component(value “role”) 告诉 Spring 容器这个 RoleIV 类是一个 Bean并且它的名称是 “role”。这样其他类可以通过 Autowired 或者 Resource 注解来注入这个名为 “role” 的 Bean 实例也可以简写成Component(“role”)甚至不给value值直接写成Component如果不写SpringIoC容器就默认类名以首字母小写的形式作为id为其生成对象装配到容器中 例如如果你有一个其他类需要使用 RoleIV可以这样做
import org.springframework.beans.factory.annotation.Autowired;
import com.ssm.pojo.RoleIV;public class SomeClass {private RoleIV role;Autowiredpublic void setRole(RoleIV role) {this.role role;}// 其他方法...
}Spring 会自动将 “role” Bean 注入到 SomeClass 的 role 字段中无需手动创建 RoleIV 实例 然而有了这个pojo类还不够SpringIoC并不知道去哪里扫描对象这个时候可以使用一个java Config告诉他
package com.ssm.pojo;import org.springframework.context.annotation.ComponentScan;/*** PojoConfig类用于配置POJO相关的设置。* 该类通过使用ComponentScan注解来自动扫描并注册所有的组件使得这些组件能够被Spring容器管理。**/
ComponentScan
public class PojoConfig {
}ComponentScan表示启动扫描默认扫描当前包的路径然后便可以通过Spring定义好的Spring IoC容器的实现类AnnotationConfigApplicationContext初始化容器并生成Bean了
package com.ssm.main;import com.ssm.pojo.RoleIV;
import com.ssm.pojo.PojoConfig;
import com.ssm.config.ApplicationConfig;
import java.sql.SQLException;import com.ssm.service.RoleIVService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;/*** 主函数类用于演示基于注解的IoC容器的使用。*/
public class AnnotationMain {/*** 程序入口点。* param args 命令行参数* throws SQLException 抛出SQLException异常*/public static void main(String[] args) throws SQLException {testAnnotation();}/*** 测试注解配置的IoC容器功能。* 该方法不接受参数也不返回任何值。* 主要步骤包括* 1. 创建基于注解的IoC容器并指定配置类* 2. 通过容器获取RoleIV类型的Bean实例* 3. 打印获取的Bean实例的id属性* 4. 关闭容器。*/public static void testAnnotation() {// 创建基于注解的IoC容器并指定配置类AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(PojoConfig.class);// 通过容器获取RoleIV类型的Bean实例RoleIV role context.getBean(RoleIV.class);// 打印获取的Bean实例的id属性System.out.println(role.getId());// 关闭容器context.close();}
}以上代码只是一个简单的例子Component存在几个非常实用的配置项例如basePackages用于配置一个Java包的数组Spring会根据它扫描对应的包和子包例如basePackageClasses用于配置多个Java类Spring会根据这个配置扫描通过如下代码可以看到效果
首先定义接口并定义接口方法代码如下
package com.ssm.service;import com.ssm.pojo.RoleIV;/*** RoleService接口定义了角色服务的相关操作。*/
public interface RoleIVService {/*** 打印角色信息。* param roleIV 角色信息实体包含角色相关的详细信息。*/public void printRoleIVInfo(RoleIV roleIV);
} Spring推荐使用接口定义方法它可以将定义和实现分离然后用集体的实现类实现 package com.ssm.service.iml;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVService;
import org.springframework.stereotype.Component;/*** RoleIVService的实现类提供具体的角色信息打印服务。*/
Component
public class RoleIVServiceImpl implements RoleIVService{/*** 打印角色信息。* param roleIV 角色信息对象包含角色的ID、名称和备注。*/Overridepublic void printRoleIVInfo(RoleIV roleIV){// 打印角色信息的方法实现System.out.println(RoleIVServiceImpl.printRoleInfo);System.out.println(roleIV.getId());System.out.println(roleIV.getRoleName());System.out.println(roleIV.getNote());}
}这里注解Component表明它是一个Spring需要装配的Bean而且也实现了对应的接口方法然后使用ComponentScan加上对应的扫描配置如下代码所示
package com.ssm.config;/*** ApplicationConfig类用于配置Spring框架的组件扫描。* 通过ComponentScan注解来告诉Spring容器去扫描指定包下的所有组件。*/import org.springframework.context.annotation.ComponentScan;
import com.ssm.pojo.RoleIV; // 引入RoleIV类用作示例
import com.ssm.service.iml.RoleIVServiceImpl; // 引入RoleIVServiceImpl类用作示例ComponentScan(basePackageClasses {RoleIV.class, RoleIVServiceImpl.class})
// 通过指定具体的类来让Spring容器扫描包含这些类的包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo, com.ssm.service})
// 通过指定包的名称来让Spring容器扫描这些包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo}, basePackageClasses {RoleIVServiceImpl.class})
// 组合使用basePackages和basePackageClasses让Spring容器扫描指定包及其子包下以及指定类所在包的组件。public class ApplicationConfig {}
// ApplicationConfig类结束应该尽量避免多个注解配置扫描混乱尤其重复扫描避免没必要的异常出现另外如果包名经常变动的场景应尽量避免使用包来扫往往包名改了如果配置没跟着都改IDE不报错这时候用类来扫会更好IDE会报错 使用如下代码来验证效果 /*** 测试组件扫描功能。* 该方法通过注解配置的应用上下文来启动Spring容器从容器中获取RoleIV和RoleIVService的实例* 然后调用服务方法处理角色信息最后关闭应用上下文。*/public static void testComponentScan() {// 创建注解配置的应用上下文并指定应用配置类ApplicationConfigAnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(ApplicationConfig.class);// 从应用上下文中获取RoleIV类型的Bean实例RoleIV role context.getBean(RoleIV.class);// 从应用上下文中获取RoleIVService类型的Bean实例RoleIVService roleIVService context.getBean(RoleIVService.class);// 调用服务方法打印角色信息roleIVService.printRoleIVInfo(role);// 关闭应用上下文context.close();}自动装配Autowired
SpringIoC先完成Bean的定义再初始化和寻找需要注入的资源所谓自动装配就是由Spring发现对应的Bean自动完成装配工作的方式如下代码所示
package com.ssm.service;/*** RoleIVServiceII接口定义了角色IV的相关服务操作*/public interface RoleIVServiceII {/*** 打印角色IV的信息* 该方法没有参数和返回值*/public void printRoleIVInfo();
}其实现类代码如下
package com.ssm.service.iml;import com.ssm.service.RoleIVServiceII;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ssm.pojo.RoleIV;/*** RoleIVServiceIIImpl类实现了RoleIVServiceII接口用于打印RoleIV对象的信息。* 通过Autowired注解自动注入RoleIV对象。*/
Component(value roleIVServiceII)
public class RoleIVServiceIIImpl implements RoleIVServiceII{Autowiredprivate RoleIV roleIV; // 自动注入的RoleIV对象/*** 打印RoleIV对象的信息包括ID、角色名和备注。* 该方法没有参数和返回值。*/Overridepublic void printRoleIVInfo() {System.out.println(roleIV.getId() roleIV.getRoleName() roleIV.getNote());}
}这里使用了Autowired表示在SpringIoC定位所有的Bean后这个字段需要按类型注入然后Spring IoC容器会寻找资源找到后将其注入也就是将实例roleIV注入进来这里需要注意的是Autowired会按照类型进行注入
SpringIoC容器有时候会寻找失败在默认情况下会抛异常因为SpringIoC容器认为一定要找到对应的Bean注入这个属性如果要改变这个特性可以通过Autowired的配置项required改变它类似这样Autowired(requiredfalse) 也就是说默认情况下值必须注入成功的也就是这个required的值默认为true声明修改为false后表明如果在已经定义好的Bean中找不到对应的类型则允许不注入也不会抛异常这个时候这个字段可能为空值需要开发者自己校验从而避免类似空指针异常等异常出现
注解Autowired除可以配置在属性外还允许方法配置常见的Bean的setter方法也可以使用它来完成注入如下代码所示
package com.ssm.service.iml;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVServiceII;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** 这是一个实现RoleIVServiceII接口的服务类用于操作RoleIV实体。*/
Component(value roleIVServiceII)
public class RoleIVServiceIIImplII implements RoleIVServiceII{private RoleIV roleIV; // RoleIV实体对象/*** 通过Autowired注解自动注入RoleIV实体。* param roleIV 要注入的RoleIV实体*/Autowiredpublic void setRoleIV(RoleIV roleIV){this.roleIV roleIV;}/*** 打印RoleIV实体的信息。* 该方法没有参数和返回值。*/public void printRoleIVInfo() {System.out.println(roleIV.getId() roleIV.getRoleName() roleIV.getNote());}
}使用Autowired这是SpringIoC自动装配完成的使得配置大幅度减少满足约定优于配置的原则也不会降低程序的健壮性 自定义装配的歧义性(注解Primary和注解Qualifier)
Spring建议在大部分情况下使用接口编程但定义一个接口并不一定只有一个实现类例如接口
package com.ssm.service;import com.ssm.pojo.RoleIV;/*** RoleService接口定义了角色服务的相关操作。*/
public interface RoleIVService {/*** 打印角色信息。* param roleIV 角色信息实体包含角色相关的详细信息。*/public void printRoleIVInfo(RoleIV roleIV);
}有了一个实现类
package com.ssm.service.iml;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVService;
import org.springframework.stereotype.Component;/*** RoleIVService的实现类提供具体的角色信息打印服务。*/
Component
public class RoleIVServiceImpl implements RoleIVService{/*** 打印角色信息。* param roleIV 角色信息对象包含角色的ID、名称和备注。*/Overridepublic void printRoleIVInfo(RoleIV roleIV){// 打印角色信息的方法实现System.out.println(RoleIVServiceImpl.printRoleInfo);System.out.println(idroleIV.getId());System.out.println(roleNameroleIV.getRoleName());System.out.println(noteroleIV.getNote());}
}还有另一个实现类
package com.ssm.service.iml;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVService;
import org.springframework.stereotype.Component;/*** RoleIVServiceImplII类实现了RoleIVService接口* 用于打印RoleIV对象的相关信息。*/
Component(RoleIVServiceImplII)
public class RoleIVServiceImplII implements RoleIVService {/*** 打印RoleIV对象的信息。** param roleIV RoleIV对象包含角色的id、角色名和备注信息。* 该方法会打印出这些信息的字符串表示。*/Overridepublic void printRoleIVInfo(RoleIV roleIV) {// 通过字符串拼接的方式将roleIV对象的id、roleName和note属性打印出来System.out.println({id roleIV.getId() , roleName roleIV.getRoleName() , note roleIV.getNote() });}
}这个时候新建一个类代码如下
package com.ssm.controller;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** RoleIVController类用于处理与RoleIV相关的请求。* 它通过注入RoleIVService来实现对RoleIV业务逻辑的调用。*/
Component
public class RoleIVController {// 自动注入RoleIVService以便在controller中使用Autowiredprivate RoleIVService roleIVService;/*** 打印RoleIV的信息。* 该方法会调用roleIVService中的printRoleIVInfo方法将roleIV的信息打印出来。* * param roleIV 角色信息对象包含角色的详细信息。*/public void printRoleIVInfo(RoleIV roleIV){roleIVService.printRoleIVInfo(roleIV);}
}它有一个字段是RoleService类型并且自动注入的属性是roleServiceRoleService是接口类型但RoleService有两个实现类这个时候SpringIoC容器就会混乱了它是无法判断入住哪个对象的于是就会抛出异常这样通过Autowired注入就会失败
究其原因发生这样的情况是因为注解Autowired采用的是按类型注入对象的方式一个接口可以有多个实现类一个抽象类也可以有多个非抽象子类但类型相同无法获取唯一的实例导致了异常情况
在SpringIoC底层容器接口BeanFactory的定义存在一个通过类型获取Bean的方法即 T T getBean(ClassT requiredType) throws BeansException; 这样仅仅通过类型(RoleIVService.class)作为参数无法判断使用哪个具体类实例返回这便是自动装备的歧义,为了解决歧义Spring提供了注解Primary和注解Qualifier用于消除歧义 Primary
Primary注解代表优先的表示当Spring IoC通过一个接口或者抽象类注入对象发现多个实现类不能做出决策时注解Primary会告诉Spring IoC容器优先将该类实例注入如下代码所示
package com.ssm.service.iml;import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVService;/*** 这是一个实现RoleIVService接口的类用于处理关于RoleIV实体的相关业务逻辑。* 通过实现printRoleIVInfo方法打印RoleIV实体的信息。** Component(roleIVServiceIII) 标注此对象是一个Bean并且在Spring容器中的名称为roleIVServiceIII。* Primary 标注此Bean在同一个接口的多个实现中具有优先权。*/
Component(roleIVServiceIII)
Primary
public class RoleIVServiceImplIII implements RoleIVService{/*** 打印RoleIV实体的信息。** param roleIV RoleIV实体包含id、角色名和备注信息。* 该方法不返回任何内容仅将实体信息以特定格式打印到控制台。*/public void printRoleIVInfo(RoleIV roleIV){// 格式化输出RoleIV实体的详细信息System.out.println({id roleIV.getId() , roleName roleIV.getRoleName() , note roleIV.getNote() });}
}Primary告诉Spring IoC容器当存在多个RoleIVService的实现类时优先将RoleIVServiceImplIII的实例注入然而Primary只能解决优先问题解决不了选择问题例如同一个接口两个实现类都挂上Primary的话代码可以这样写但是注入的时候就会抛异常了 Qualifier
出现歧义的一个重要原因是Spring在注入时是按类型除了按类型查找BeanSpringIoC容器底层的接口BeanFactory也定义了按名称查找的方法T T getBean(String name, ClassT requiredType) throws BeansException;,注解Qualifier就是用来按名称查找的修改RoleIVController代码如下
package com.ssm.controller;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;/*** RoleIVController类用于处理与RoleIV相关的请求。* 它通过注入RoleIVService来实现对RoleIV业务逻辑的调用。*/
Component
public class RoleIVController {// 自动注入RoleIVService以便在controller中使用AutowiredQualifier(roleIVServiceIII)private RoleIVService roleIVService;/*** 打印RoleIV的信息。* 该方法会调用roleIVService中的printRoleIVInfo方法将roleIV的信息打印出来。** param roleIV 角色信息对象包含角色的详细信息。*/public void printRoleIVInfo(RoleIV roleIV){roleIVService.printRoleIVInfo(roleIV);}
} 这个时候IoC容器不会再按照类型的方式注入而是按照名称的方式注入就不会存在歧义再明确一下Autowired的注入规则先按照类型匹配如果只有一个满足的Bean则直接将其注入结束如果找到多个匹配的Bean类型,那么会按属性名查找Bean例如上边这个代码private RoleIVService roleIVService;,就会先找roleIVService这个字符串如果可以找到则结束注入过程在这两个情况都找不到并且又不允许注入为空时则抛异常 装载带有参数的构造方法类
通常构造方法都是带参数的而带参数的构造方法也允许通过注解注入如下代码所示
package com.ssm.controller;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleIVService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;/*** RoleIVController类负责处理RoleIV相关的控制器逻辑。* 依赖于RoleIVService来执行具体的服务逻辑。*/
Component
public class RoleIVController {// RoleIVService的实例用于执行角色IV相关的服务操作。private RoleIVService roleIVService;/*** RoleIVController的构造函数通过依赖注入的方式初始化roleIVService。** param roleIVService 一个标记为Qualifier(roleIVServiceIII)的RoleIVService实例用于具体的服务逻辑处理。*/public RoleIVController(Autowired Qualifier(roleIVServiceIII) RoleIVService roleIVService){this.roleIVService roleIVService;}/*** 打印RoleIV的信息。* 该方法会调用roleIVService中的printRoleIVInfo方法将roleIV的信息打印出来。** param roleIV 角色信息对象包含角色的详细信息。*/public void printRoleIVInfo(RoleIV roleIV){roleIVService.printRoleIVInfo(roleIV);}
}
使用注解Bean装配
以上都是通过Component装配Bean但注解Component只能注解在类上不能注解到方法上对于Java而言大部分的开发都需要引入第三方的包而且往往并没有这些包的资源这时候将无法为这些包的类加入注解Component,从而让它成为开发环境的Bean
这种情况开发者可以使用新类扩展(extends)其包内的类然后在新类上使用注解Component但这样又会显得很奇怪这个场景中Spring给了一个注解Bean它可以注解到方法上并将方法返回的对象作为Spring的Bean存放在IoC的容器中
例如如我需要使用DBCP数据源就要引入关于它的包然后装配数据源的Bean如下代码所示
package com.ssm.config;import java.util.Properties;
import javax.sql.DataSource;import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;/*** 数据源配置类用于配置数据库连接池。*/
public class DataSourceConfig {// 通过注解方式注入MySQL驱动类名Value(com.mysql.jdbc.Driver)private String driverClassName null;// 通过注解方式注入数据库URLValue(jdbc:mysql://localhost:3306/ssm)private String url null;// 通过注解方式注入数据库用户名Value(root)private String username null;// 通过注解方式注入数据库密码Value(Ms123!#)private String password null;/*** 创建并返回DataSource Bean。** return 返回配置好的DataSource实例。*/Bean(dataSource)public DataSource getDataSource(){Properties props new Properties();// 设置数据库连接池的属性props.setProperty(driverClassName, driverClassName);props.setProperty(url, url);props.setProperty(username, username);props.setProperty(password, password);DataSource datasource null; // 初始化数据源变量try{// 通过属性创建数据源datasource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return datasource;}
}这样就能够装配一个Bean当SpringIoC容器扫描它的时候就会为其生成对应的Bean和其他的Bean一样它可以通过Autowired或者Qualifier等注解注入别的Bean中
注解自定义Bean的初始化和销毁方法
在Spring框架中Bean注解告诉Spring容器这个方法将会返回一个需要被管理的对象即Bean在该注解中可以自定义初始化方法和销毁方法等一系列操作只需要运用注解Bean的配置项
Bean的名称默认情况下Bean注解的方法名就是Bean的ID。如果需要自定义Bean的名称可以使用name属性 如Bean(name myBean)。如果有多个Bean方法返回相同类型可以通过Qualifier注解来区分它们。初始化和销毁方法initMethod和destroyMethod属性可以用来指定Bean初始化和销毁时要调用的方法。例如Bean(initMethod init, destroyMethod cleanup)。依赖注入Spring会自动处理Bean方法之间的依赖关系通过方法调用来注入。例如如果Bean A需要Bean B可以直接在A的Bean方法中调用B的Bean方法。如果需要注入其他已经注册的Bean可以使用Autowired注解或者通过Qualifier来指定特定的Bean。作用域默认情况下Bean创建的Bean是单例(Singleton)的。如果你想创建原型(Prototype)或者其他作用域的Bean可以使用scope属性如Bean(scope ConfigurableBeanFactory.SCOPE_PROTOTYPE)。autowireCandidate属性是一个布尔值它用于控制Spring容器在自动装配其他Bean时是否考虑当前Bean。默认情况下autowireCandidate是true意味着这个Bean可以作为自动装配的候选者。如果设置为false那么这个Bean将被排除在自动装配的候选列表之外。当你有一个Bean你并不希望它在自动装配过程中被其他组件使用或者你有多个相同类型的Bean但只想让其中一个参与自动装配这时就可以使用autowireCandidate。属性值注入可以使用Value注解来注入属性值比如常量或环境变量。对于复杂的属性配置可以使用ConfigurationProperties注解配合YAML或Properties文件来绑定。ProfileProfile注解允许你在特定的环境中激活或禁用Bean。例如Bean(Profile(dev))将只在开发环境下创建Bean。懒加载使用Lazy注解标记的Bean表示该Bean将在第一次请求时才创建而不是在容器启动时立即创建。复合BeanBean方法可以返回另一个Bean方法的引用实现Bean的组合。自定义初始化逻辑Bean方法体中的代码会作为Bean的初始化逻辑执行。代理模式Spring支持JDK动态代理和CGLIB代理你可以通过proxyMode属性来控制。 Bean注解通常与Configuration注解一起使用后者标识一个类作为配置源提供了声明式的方式来定义Bean。这种方式比XML配置更简洁且易于维护 /*** 创建并配置 SoybeanMilkMakerII 实例的 Bean。** return SoybeanMilkMakerII 实例配置了特定的饮料店名称和制作原料来源。* see SoybeanMilkMakerII*/Bean(name soybeanMilkMakerII, initMethod init, destroyMethod Mydestroy)public SoybeanMilkMakerII getSoybeanMilkMakerII() {// 创建 SoybeanMilkMakerII 实例SoybeanMilkMakerII soybeanMilkMakerII new SoybeanMilkMakerII();// 设置所属饮料店为贡茶soybeanMilkMakerII.setBeverageShop(贡茶);// 创建原料来源并配置其属性Source source new Source(豆子, 少糖, 中杯);soybeanMilkMakerII.setSource(source);return soybeanMilkMakerII;}混合使用装配Bean
在使用注解的前提下有些场景使用XML会更合适这就产生了两个共同存在的情况例如当引入了第三方包或者服务的时候如果使用注解那么这个第三方的配置可能会散落在各个地方难于管理这种情况下用XML会更合适
例如如下代码
package com.ssm.config;import java.util.Properties;
import javax.sql.DataSource;import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;/*** 数据源配置类用于配置数据库连接池。*/
public class DataSourceConfig {// 通过注解方式注入MySQL驱动类名Value(com.mysql.jdbc.Driver)private String driverClassName null;// 通过注解方式注入数据库URLValue(jdbc:mysql://localhost:3306/ssm)private String url null;// 通过注解方式注入数据库用户名Value(root)private String username null;// 通过注解方式注入数据库密码Value(xxxxxx)private String password null;/*** 创建并返回DataSource Bean。** return 返回配置好的DataSource实例。*/Bean(dataSource)public DataSource getDataSource(){Properties props new Properties();// 设置数据库连接池的属性props.setProperty(driverClassName, driverClassName);props.setProperty(url, url);props.setProperty(username, username);props.setProperty(password, password);DataSource datasource null; // 初始化数据源变量try{// 通过属性创建数据源datasource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return datasource;}
}
完全可以用
?xml version1.0 encodingUTF-8 ?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:chttp://www.springframework.org/schema/cxmlns:phttp://www.springframework.org/schema/pxmlns:utilhttp://www.springframework.org/schema/utilxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd bean iddataSource classorg.apache.commons.dbcp2.BasicDataSourceproperty namedriverClassName valuecom.mysql.jdbc.Driver /property nameurl valuejdbc:mysql://localhost:3306/ssm /property nameusername valueroot /property namepassword valuexxxxx //bean/beans假设有这个xmlxml定义了一个bean其内容是数据库连接配置就可以将这个xml引入到注解的体系中如下代码所示
package com.ssm.config;/*** ApplicationConfig类用于配置Spring框架的组件扫描。* 通过ComponentScan注解来告诉Spring容器去扫描指定包下的所有组件。*/import org.springframework.context.annotation.ComponentScan;
import com.ssm.pojo.RoleIV; // 引入RoleIV类用作示例
import com.ssm.service.iml.RoleIVServiceImpl; // 引入RoleIVServiceImpl类用作示例
import org.springframework.context.annotation.ImportResource;ComponentScan(basePackageClasses {RoleIV.class, RoleIVServiceImpl.class})
// 通过指定具体的类来让Spring容器扫描包含这些类的包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo, com.ssm.service})
// 通过指定包的名称来让Spring容器扫描这些包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo}, basePackageClasses {RoleIVServiceImpl.class})
// 组合使用basePackages和basePackageClasses让Spring容器扫描指定包及其子包下以及指定类所在包的组件。
/*** 导入资源文件到Spring上下文中。* 该注解用于指定Spring配置文件的路径以便Spring容器在启动时加载该配置文件。* 具体路径为classpath下的spring-data.xml文件。* 该注解通常用于配置与数据访问相关的bean例如Hibernate的sessionFactory或JPA的entityManagerFactory。*/
ImportResource({classpath:spring-data.xml})public class ApplicationConfig {}
// ApplicationConfig类结束
ImportResource中配置的内容是个数组它可以配置多个XML配置文件这样就可以引入多个XML定义的Bean了
先定义一个接口如下所示
/*** RoleDataSourceService接口定义了角色数据源服务的相关操作。* 该接口主要负责通过ID获取角色信息的具体实现。*/
package com.ssm.service;import com.ssm.pojo.RoleIV;public interface RoleDataSourceService {/*** 根据指定ID获取角色信息。** param id 角色的唯一标识符类型为long。* return 返回对应ID的角色信息类型为RoleIV。*/public RoleIV getRoleIV(Long id);
}其实现类代码如下
package com.ssm.service.iml;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleDataSourceService;/*** 实现RoleDataSourceService接口提供获取角色信息的功能*/
Service
Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class RoleDataSourceServiceImpl implements RoleDataSourceService {// 通过自动装配获取数据源AutowiredDataSource dataSource null;/*** 根据角色ID获取角色信息* param id 角色的ID* return 返回对应的角色信息如果没有找到则返回null*/Overridepublic RoleIV getRoleIV(Long id) {Connection conn null;ResultSet rs null;PreparedStatement ps null;RoleIV roleIV null;try {// 获取数据库连接conn dataSource.getConnection();// 构造查询SQL语句String sql select id, role_name, note from t_role where id ?;ps conn.prepareStatement(sql);// 设置查询参数ps.setLong(1, id);// 执行查询rs ps.executeQuery();// 处理查询结果while (rs.next()) {// 构建角色对象roleIV new RoleIV();roleIV.setId(rs.getLong(id));roleIV.setRoleName(rs.getString(role_name));roleIV.setNote(rs.getString(note));}} catch (SQLException e) {e.printStackTrace();} finally {// 关闭数据库连接及相关资源if (conn ! null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}return roleIV;}
}通过这样的形式就把XML配置的dataSource注入RoleIVDataSourceServiceImpl了,而有的时候所有的配置都放在一个ApplicationConfig类里会造成配置过多且复杂因此开发者可能希望有多个类似于ApplicationConfig的配置类例如ApplicationConfig2、ApplicationConfig3等Spring也提供了这个机制使用Import的方式注入这些配置类 package com.ssm.config;/*** ApplicationConfig类用于配置Spring框架的组件扫描。* 通过ComponentScan注解来告诉Spring容器去扫描指定包下的所有组件。*/import com.ssm.pojo.PojoConfig;
import org.springframework.context.annotation.ComponentScan;
import com.ssm.pojo.RoleIV; // 引入RoleIV类用作示例
import com.ssm.service.iml.RoleIVServiceImpl; // 引入RoleIVServiceImpl类用作示例
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;ComponentScan(basePackageClasses {RoleIV.class, RoleIVServiceImpl.class})
// 通过指定具体的类来让Spring容器扫描包含这些类的包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo, com.ssm.service})
// 通过指定包的名称来让Spring容器扫描这些包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo}, basePackageClasses {RoleIVServiceImpl.class})
// 组合使用basePackages和basePackageClasses让Spring容器扫描指定包及其子包下以及指定类所在包的组件。
/*** 导入资源文件到Spring上下文中。* 该注解用于指定Spring配置文件的路径以便Spring容器在启动时加载该配置文件。* 具体路径为classpath下的spring-data.xml文件。* 该注解通常用于配置与数据访问相关的bean例如Hibernate的sessionFactory或JPA的entityManagerFactory。*/
ImportResource({classpath:spring-data.xml})
/*** 使用Import注解引入配置类* 该注解用于指定应用在启动时需要加载的配置类。在这里我们引入了两个配置类* 1. DataSourceConfig.class用于数据源的配置例如数据库连接池的配置。* 2. PojoConfig.class用于POJOPlain Old Java Object的配置例如实体类的映射配置。* 这两个配置类会被Spring上下文加载以便应用在运行时可以使用其中定义的配置。*/
Import({DataSourceConfig.class, PojoConfig.class})public class ApplicationConfig {}
// ApplicationConfig类结束
在XML中也已使用这个机制如下代码所示在spring-bean.xml中引入spring-datasource.xml就可以在spring-bean.xml中使用import元素来加载如代码所示import resourcespring-datasource.xmlSpring不支持使用XML加载Java配置类但Spring支持通过XML的配置扫描注解的包如代码所示context:component-scan base-packagecom.ssm/
使用Profile
实际开发中都存在多个环境这样就有了在不同的系统中进行切换的需求Spring也支持这样的场景在Spring中我们可以定义Bean的Profile
使用注解Profile配置
package com.ssm.bean;
import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
/*** ProfileDataSource类用于根据不同的环境配置数据源。* 通过Profile注解实现了根据Spring环境dev或test动态配置数据源。*/
Component
public class ProfileDataSource {/*** 当环境为dev时创建并返回一个数据源。** return DataSource 返回一个数据源实例。*/Bean(name devDataSource)Profile(dev)public DataSource getDevDataSource() {System.out.println(dev datasource);// 配置数据库连接属性Properties props new Properties();props.setProperty(driver, com.mysql.jdbc.Driver);props.setProperty(url, jdbc:mysql://localhost:3306/ssm);props.setProperty(username, root);props.setProperty(password, 123456);DataSource dataSource null;// 尝试根据配置创建数据源try {dataSource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return dataSource;}/*** 当环境为test时创建并返回一个数据源。** return DataSource 返回一个数据源实例。*/Bean(name testDataSource)Profile(test)public DataSource getTestDataSource() {System.out.println(test datasource);// 配置数据库连接属性此处与dev环境配置相同实际应用中可能不同Properties props new Properties();props.setProperty(driver, com.mysql.jdbc.Driver);props.setProperty(url, jdbc:mysql://localhost:3306/ssm);props.setProperty(username, root);props.setProperty(password, 123456);DataSource dataSource null;// 尝试根据配置创建数据源try {dataSource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return dataSource;}
}如代码所示使用注解Profile配置了两个环境的数据库连接池
使用XML定义Profile
?xml version1.0 encodingUTF-8 ?
!-- 定义Spring配置文件指定配置文件的版本和命名空间 --
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:phttp://www.springframework.org/schema/pxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsdprofiledev!-- 定义数据源bean使用Apache Commons DBCP2提供的BasicDataSource --bean iddataSource classorg.apache.commons.dbcp2.BasicDataSource!-- 设置数据库驱动类名 --property namedriverClassName valuecom.mysql.jdbc.Driver /!-- 设置数据库连接URL --property nameurl valuejdbc:mysql://localhost:3306/ssm /!-- 设置数据库用户名 --property nameusername valueroot /!-- 设置数据库密码 --property namepassword value123456 //bean
/beans如上代码配置了一个Profile为dev的数据源,也可以配置多个Profile ?xml version1.0 encodingUTF-8?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:phttp://www.springframework.org/schema/pxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsd!-- 定义在测试环境下的数据源配置 --beans profiletestbean iddevDataSource classorg.apache.commons.dbcp2.BasicDataSource!-- 数据库驱动类名 --property namedriverClassName valuecom.mysql.jdbc.Driver /!-- 数据库连接URL --property nameurl valuejdbc:mysql://localhost:3306/ssm /!-- 数据库用户名 --property nameusername valueroot /!-- 数据库密码 --property namepassword value123456 //bean/beans!-- 定义在开发环境下的数据源配置配置内容与测试环境相同 --beans profiledevbean iddevDataSource classorg.apache.commons.dbcp2.BasicDataSourceproperty namedriverClassName valuecom.mysql.jdbc.Driver /property nameurl valuejdbc:mysql://localhost:3306/ssm /property nameusername valueroot /property namepassword value123456 //bean/beans
/beans如果使用的IDE是Idea会弹出通知点开后便可以选择配置
启动Profile
当启动Java或者XML配置的Profile时这些Bean并不会被加载到Spring IoC容器中需要自行激活Profile常见激活Profile的方法有5种
在使用Spring MVC的情况下可以配置Web上下文参数或者配置DispatchServlet参数作为JNDI条目配置环境变量配置JVM启动参数在集成测试环境中使用注解ActiveProfile
package com.ssm;import javax.sql.DataSource;import com.ssm.config.ApplicationConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;RunWith(SpringJUnit4ClassRunner.class) // 使用Spring提供的JUnit运行器
ContextConfiguration(classes ApplicationConfig.class) // 指定Spring配置类
ActiveProfiles(test) // 指定使用的Spring Profile为test
public class ProfileTest {Autowiredprivate DataSource dataSource; // 自动注入数据源/*** 测试方法用于验证在“test”环境下数据源是否能被正确注入。* 该方法没有参数和返回值。*/Testpublic void test() {System.out.println(dataSource.getClass().getName()); // 输出数据源类的名称}
}以上代码是通过ActiveProfiles注解来指定加载哪个Profile有些时候程序要在一些服务器上运行这个时候可以配置Java虚拟机的启动项例如程序在Tomcat服务器上或者main方法上运行Java虚拟机的参数如下
spring.profiles.active:如果配置了该参数那么spring.profiles.default配置项将失效spring.profiles.default:默认的配置如果没有配置关于Profile的参数就使用这个默认配置
在上边这个例子中配置应该是JAVA_OPTS-Dspring.profiles.activetest 都可以配置启动项也可以根据自己的项目属性自行配置 如果是使用Spring MVC的Web项目也可以设置Web环境参数或者DispatcherServlet参数选择对应的Profile例如修改web.xml配置Profile
!DOCTYPE web-app PUBLIC-//Sun Microsystems, Inc.//DTD Web Application 2.3//ENhttp://java.sun.com/dtd/web-app_2_3.dtd web-app!-- 配置Spring IoC配置文件路径 --context-paramparam-namecontextConfigLocation/param-nameparam-value/WEB-INF/applicationContext.xml/param-value/context-param!-- 设置Spring环境配置参数 --context-param!-- 参数名称指定Spring环境的活动配置文件 --param-namespring.profiles.active/param-name!-- 参数值定义当前活动的环境配置此处为test环境 --param-valuetest/param-value/context-param!-- 配置ContextLoaderListener用以初始化Spring IoC容器 --listenerlistener-classorg.springframework.web.context.ContextLoaderListener/listener-class/listener!-- 配置DispatcherServlet --servlet!-- 注意Spring MVC框架会根据servlet-name配置找到/WEB-INF/dispatcher-servlet.xml作为配置文件载入Web工程中 --servlet-namedispatcher/servlet-nameservlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class!-- 初始化参数配置 --init-param!-- 参数名称用于指定Spring环境配置的激活 profil --param-namespring.profiles.active/param-name!-- 参数值此处设置为test表示激活的环境配置为test --param-valuetest/param-value/init-param!-- 使得Dispatcher在服务器启动的时候就初始化 --load-on-startup2/load-on-startup/servlet!-- Servlet拦截配置 --servlet-mappingservlet-namedispatcher/servlet-nameurl-pattern//url-pattern/servlet-mapping
/web-app 注意顺序顺序反了要飘红的 加载属性文件
使用注解方式加载属性文件
同样是在ApplicationConfig.java文件中写入如下配置
/*** 使用该注解来向Spring Boot应用添加一个属性源。它会尝试从类路径下的database-config.properties文件中加载属性* 并将其命名为database.properties。如果文件找不到则不会报错因为设置了ignoreResourceNotFound为true。* 该属性源的编码格式为UTF-8。** 该注解通常用于配置类上以在Spring应用启动时加载额外的配置属性。*/
PropertySource(value classpath:database-config.properties,name database.properties,ignoreResourceNotFound true,encoding UTF-8
)这样还不够Spring还无法将属性读入因为缺少属性文件的解析配置器,即PropertySourcesPlaceHolderConfigurer需在继续添加如下代码
package com.ssm.config;/*** ApplicationConfig类用于配置Spring框架的组件扫描。* 通过ComponentScan注解来告诉Spring容器去扫描指定包下的所有组件。*/import com.ssm.condition.DataSourceCondition;
import com.ssm.pojo.PojoConfig;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import com.ssm.pojo.RoleIV; // 引入RoleIV类用作示例
import com.ssm.service.iml.RoleIVServiceImpl; // 引入RoleIVServiceImpl类用作示例
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;import javax.sql.DataSource;
import java.util.Properties;ComponentScan(basePackageClasses {RoleIV.class, RoleIVServiceImpl.class})
// 通过指定具体的类来让Spring容器扫描包含这些类的包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo, com.ssm.service})
// 通过指定包的名称来让Spring容器扫描这些包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo}, basePackageClasses {RoleIVServiceImpl.class})
// 组合使用basePackages和basePackageClasses让Spring容器扫描指定包及其子包下以及指定类所在包的组件。
/*** 导入资源文件到Spring上下文中。* 该注解用于指定Spring配置文件的路径以便Spring容器在启动时加载该配置文件。* 具体路径为classpath下的spring-data.xml文件。* 该注解通常用于配置与数据访问相关的bean例如Hibernate的sessionFactory或JPA的entityManagerFactory。*/
ImportResource({classpath:spring-data.xml})
/*** 使用Import注解引入配置类* 该注解用于指定应用在启动时需要加载的配置类。在这里我们引入了两个配置类* 1. DataSourceConfig.class用于数据源的配置例如数据库连接池的配置。* 2. PojoConfig.class用于POJOPlain Old Java Object的配置例如实体类的映射配置。* 这两个配置类会被Spring上下文加载以便应用在运行时可以使用其中定义的配置。*/
Import({DataSourceConfig.class, PojoConfig.class})/*** 使用该注解来向Spring Boot应用添加一个属性源。它会尝试从类路径下的database-config.properties文件中加载属性* 并将其命名为database.properties。如果文件找不到则不会报错因为设置了ignoreResourceNotFound为true。* 该属性源的编码格式为UTF-8。** 该注解通常用于配置类上以在Spring应用启动时加载额外的配置属性。*/
PropertySource(value classpath:database-config.properties,name database.properties,ignoreResourceNotFound true,encoding UTF-8
)public class ApplicationConfig {/*** 创建并返回一个PropertySourcesPlaceholderConfigurer的实例。* 这个配置器是用来处理属性文件占位符的它可以在Spring配置文件中启用属性文件的引用。* 通过这个方法可以在Spring Bean的配置中使用占位符来引用属性文件中的属性值。** return PropertySourcesPlaceholderConfigurer 返回一个配置了属性文件解析器的实例。*/Beanpublic PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}/*** 创建并配置数据源 bean。** param driver JDBC驱动程序的类名。* param url 数据库连接的URL。* param username 连接数据库所需的用户名。* param password 连接数据库所需的密码。* return 配置好的数据源实例。*/Bean(name dataSource)public DataSource getDataSource(// 从属性文件中注入的JDBC配置Value(${jdbc.database.driver}) String driver,Value(${jdbc.database.url}) String url,Value(${jdbc.database.username}) String username,Value(${jdbc.database.password}) String password) {// 设置数据库连接的属性Properties props new Properties();props.setProperty(driver, driver);props.setProperty(url, url);props.setProperty(username, username);props.setProperty(password, password);DataSource dataSource null;// 尝试根据属性创建数据源try {dataSource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {// 异常处理打印堆栈跟踪e.printStackTrace();}return dataSource;}}
测试配置代码如下 /*** 测试应用程序配置属性的方法。* 该方法通过IoC容器获取应用程序配置中的数据库连接信息并打印出来。** throws SQLException 如果获取数据库连接或元数据时发生错误*/public static void testProperties() throws SQLException {// 创建IoC容器使用注解配置ApplicationContext context new AnnotationConfigApplicationContext(ApplicationConfig.class);// 获取应用程序的环境配置Environment env context.getEnvironment();// 从环境配置中获取jdbc数据库urlString url env.getProperty(jdbc.database.url);// 从IoC容器中获取DataSource BeanDataSource ds context.getBean(DataSource.class);// 打印数据库连接的URLSystem.out.println(ds.getConnection().getMetaData().getURL());}在ApplicationConfig.java文件中写了如下方法并加上了Bean注解 /*** 创建并返回一个PropertySourcesPlaceholderConfigurer的实例。* 这个配置器是用来处理属性文件占位符的它可以在Spring配置文件中启用属性文件的引用。* 通过这个方法可以在Spring Bean的配置中使用占位符来引用属性文件中的属性值。** return PropertySourcesPlaceholderConfigurer 返回一个配置了属性文件解析器的实例。*/Beanpublic PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}其作用是让Spring能够解析属性占位符能够解析属性占位符Spring提供了Value注解和占位符的形式来使用占位符 先看一下属性文件database-config.properties情况
jdbc.database.drivercom.mysql.cj.jdbc.Driver
jdbc.database.urljdbc:mysql://localhost:3306/ssm
jdbc.database.usernameroot
jdbc.database.passwordMs123!#package com.ssm.config;import java.util.Properties;
import javax.sql.DataSource;import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;/*** 数据源配置类用于配置数据库连接池。*/
Component
public class DataSourceConfigII {// 通过注解方式注入MySQL驱动类名Value(${jdbc.database.driver})private String driverClassName null;// 通过注解方式注入数据库URLValue(${jdbc.database.url})private String url null;// 通过注解方式注入数据库用户名Value(${jdbc.database.username})private String username null;// 通过注解方式注入数据库密码Value(${jdbc.database.password})private String password null;/*** 创建并返回DataSource Bean。** return 返回配置好的DataSource实例。*/Bean(dataSource)public DataSource getDataSource(){Properties props new Properties();// 设置数据库连接池的属性props.setProperty(driverClassName, driverClassName);props.setProperty(url, url);props.setProperty(username, username);props.setProperty(password, password);DataSource datasource null; // 初始化数据源变量try{// 通过属性创建数据源datasource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return datasource;}
}
使用XML加载属性文件
用XML的方式也可以加载属性文件只需要使用comtext:property-placeholder配置项如下代码所示
?xml version1.0 encodingUTF-8 ?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:phttp://www.springframework.org/schema/pxmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd!-- 配置属性占位符指定属性文件位置并设置是否忽略未找到的资源 --context:property-placeholderignore-resource-not-foundfalselocationclasspath:database-config.properties /!-- 数据源配置使用Apache Commons DBCP2提供的BasicDataSource实现 --bean iddataSource classorg.apache.commons.dbcp2.BasicDataSource!-- 通过属性文件中的配置动态设置数据源的属性 --property namedriverClassName value${jdbc.database.driver} /property nameurl value${jdbc.database.url} /property nameusername value${jdbc.database.username} /property namepassword value${jdbc.database.password} //bean
/beans
特别要注意ignore-resource-not-foundfalse, 表示是否允许文件不存在为false时不允许文件不存在如果不存在Spring会抛出异常locationclasspath:database-config.propertieslocation是文件路径可以配置单个或多个用逗号隔开即可
也可以使用如下方式避免配置多个属性文件的时候过长可读性差
?xml version1.0 encodingUTF-8 ?
beans xmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:phttp://www.springframework.org/schema/pxmlns:contexthttp://www.springframework.org/schema/contextxsi:schemaLocationhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd!-- 配置属性占位符指定属性文件位置并设置是否忽略未找到的资源 --!--context:property-placeholderignore-resource-not-foundfalselocationclasspath:database-config.properties /--!-- 数据源配置使用Apache Commons DBCP2提供的BasicDataSource实现 --bean iddataSource classorg.apache.commons.dbcp2.BasicDataSource!-- 通过属性文件中的配置动态设置数据源的属性 --property namedriverClassName value${jdbc.database.driver} /property nameurl value${jdbc.database.url} /property nameusername value${jdbc.database.username} /property namepassword value${jdbc.database.password} //bean!-- 定义一个bean来配置属性源占位符解析器 --bean idpropertySourcesPlaceholderConfigurerclassorg.springframework.context.support.PropertySourcesPlaceholderConfigurer!-- 配置属性文件的位置 --property namelocationsarray!-- 指定数据库配置文件的位置 --valueclasspath:database-config.properties/value!-- 指定日志配置文件的位置 --valuelog4j.properties/value/array/property!-- 配置是否忽略资源未找到的错误 --property nameignoreResourceNotFound valuetrue //bean
/beans条件化装配Bean
在某些条件下不需要装配某些Bean比如当没有database-config.properties属性配置时就不需要创建数据源需要有个条件判断Spring提供了注解Conditional去配置通过它可以配置一个或者多个类只需要这些类实现实现Condition接口即可(org.springframework.context.annotation.Condition)代码如下
package com.ssm.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.stereotype.Component;/*** 数据源条件类用于根据环境配置决定是否加载特定的Bean。* 实现了Spring的Condition接口重写了matches方法来判断条件是否满足。*/
Component
public class DataSourceCondition implements Condition {/*** 判断条件是否满足。* param context 条件上下文提供了环境和类型元数据的信息。* param metadata 注解类型元数据用于获取类上的注解信息。* return boolean 返回true表示条件满足false表示条件不满足。*/Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Environment env context.getEnvironment();// 检查环境配置中是否包含了必要的jdbc属性return env.containsProperty(jdbc.database.driver) env.containsProperty(jdbc.database.url) env.containsProperty(jdbc.database.username) env.containsProperty(jdbc.database.password);}
}DataSourceCondition 实现了Condition 接口的matches方法该方法有两个参数一个是ConditionContext它可以获得Spring的运行环境另一个是AnnotatedTypeMetadata它可以获取关于该Bean的注解信息这段代码优先获取了运行上下文的环境然后判断在环境中属性文件是否配置了数据库的相关参数如果配置了返回trueSpring创建对应的Bean否则不创建接着就可以配置数据源了
package com.ssm.config;/*** ApplicationConfig类用于配置Spring框架的组件扫描。* 通过ComponentScan注解来告诉Spring容器去扫描指定包下的所有组件。*/import com.ssm.condition.DataSourceCondition;
import com.ssm.pojo.PojoConfig;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import com.ssm.pojo.RoleIV; // 引入RoleIV类用作示例
import com.ssm.service.iml.RoleIVServiceImpl; // 引入RoleIVServiceImpl类用作示例
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;import javax.sql.DataSource;
import java.util.Properties;ComponentScan(basePackageClasses {RoleIV.class, RoleIVServiceImpl.class})
// 通过指定具体的类来让Spring容器扫描包含这些类的包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo, com.ssm.service})
// 通过指定包的名称来让Spring容器扫描这些包及其子包下的所有组件。ComponentScan(basePackages {com.ssm.pojo}, basePackageClasses {RoleIVServiceImpl.class})
// 组合使用basePackages和basePackageClasses让Spring容器扫描指定包及其子包下以及指定类所在包的组件。
/*** 导入资源文件到Spring上下文中。* 该注解用于指定Spring配置文件的路径以便Spring容器在启动时加载该配置文件。* 具体路径为classpath下的spring-data.xml文件。* 该注解通常用于配置与数据访问相关的bean例如Hibernate的sessionFactory或JPA的entityManagerFactory。*/
ImportResource({classpath:spring-data.xml})
/*** 使用Import注解引入配置类* 该注解用于指定应用在启动时需要加载的配置类。在这里我们引入了两个配置类* 1. DataSourceConfig.class用于数据源的配置例如数据库连接池的配置。* 2. PojoConfig.class用于POJOPlain Old Java Object的配置例如实体类的映射配置。* 这两个配置类会被Spring上下文加载以便应用在运行时可以使用其中定义的配置。*/
Import({DataSourceConfig.class, PojoConfig.class})/*** 使用该注解来向Spring Boot应用添加一个属性源。它会尝试从类路径下的database-config.properties文件中加载属性* 并将其命名为database.properties。如果文件找不到则不会报错因为设置了ignoreResourceNotFound为true。* 该属性源的编码格式为UTF-8。** 该注解通常用于配置类上以在Spring应用启动时加载额外的配置属性。*/
PropertySource(value classpath:database-config.properties,name database.properties,ignoreResourceNotFound true,encoding UTF-8
)public class ApplicationConfig {/*** 创建并返回一个PropertySourcesPlaceholderConfigurer的实例。* 这个配置器是用来处理属性文件占位符的它可以在Spring配置文件中启用属性文件的引用。* 通过这个方法可以在Spring Bean的配置中使用占位符来引用属性文件中的属性值。** return PropertySourcesPlaceholderConfigurer 返回一个配置了属性文件解析器的实例。*/Beanpublic PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}/*** 创建并配置数据源 bean。** param driver JDBC驱动程序的类名。* param url 数据库连接的URL。* param username 连接数据库所需的用户名。* param password 连接数据库所需的密码。* return 配置好的数据源实例。*/Bean(name dataSource)// 根据特定条件决定是否创建该beanConditional({DataSourceCondition.class})public DataSource getDataSource(Value(${jdbc.database.driver}) String driver, Value(${jdbc.database.url}) String url, Value(${jdbc.database.username}) String username, Value(${jdbc.database.password}) String password) {// 设置数据库连接的属性Properties props new Properties();props.setProperty(driver, driver);props.setProperty(url, url);props.setProperty(username, username);props.setProperty(password, password);DataSource dataSource null;// 尝试根据属性创建数据源try {dataSource BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {// 异常处理打印堆栈跟踪e.printStackTrace();}return dataSource;}
}Bean的作用域
所谓Bean的作用域是指Bean在应用中的有效范围默认情况下SpringIoC容器只会对Bean创建唯一实例然后在Spring IoC容器的生命周期中有效用如下代码测试一下 /*** 测试作用域* 该方法通过创建一个注解配置的应用上下文并从该上下文中获取RoleDataSourceService类型的bean实例* 以此来验证Spring容器中bean的作用域特性。* 该方法不接受参数且没有返回值。*/public static void testScope() {// 创建一个注解配置的应用上下文并指定配置类ApplicationConfigAnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(ApplicationConfig.class);// 从上下文中获取RoleDataSourceService类型的bean实例RoleDataSourceService service1 context.getBean(RoleDataSourceService.class);RoleDataSourceService service2 context.getBean(RoleDataSourceService.class);// 打印两个bean实例是否为同一个对象的引用System.out.println(service1 service2);// 关闭应用上下文context.close();}这里Spring IoC容器通过类型的方式获取Bean然后通过比较两次获取的Bean的结果这是一个位比较比较service1和service2是否为同一个对象很显然结果会返回true换句话说在默认情况下SpringIoC容器只会为配置的Bean生成一个实例而不是多个
而在互联网对性能有基本要求的场景下有时候我们希望每请一次就产生一个独立的对象这样多个实例可以在不同的线程运行在对性能有要求的场景中就能发挥作用这些是由Spring的作用域决定的Spring IoC容器提供了2种作用域(单例和原型)单例(singleton)是默认选项在整个应用中Spring只为其生成一个Bean的实例原型(prototype)当每次从Spring IoC容器获取Bean时Spring都会为它创建一个新的实例
在Spring中可以用注解Scope指定作用域如下代码所示
package com.ssm.service.iml;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import com.ssm.pojo.RoleIV;
import com.ssm.service.RoleDataSourceService;/*** 实现RoleDataSourceService接口提供获取角色信息的功能*/
Service
/*** RoleDataSourceServiceImpl 类实现了 RoleDataSourceService 接口* 用于提供角色相关的数据源服务。该类的作用范围被注解为原型作用域SCOPE_PROTOTYPE* 意味着每次请求都会创建一个新的实例。*/
Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RoleDataSourceServiceImpl implements RoleDataSourceService {// 通过自动装配获取数据源AutowiredDataSource dataSource null;/*** 根据角色ID获取角色信息* param id 角色的ID* return 返回对应的角色信息如果没有找到则返回null*/Overridepublic RoleIV getRoleIV(Long id) {Connection conn null;ResultSet rs null;PreparedStatement ps null;RoleIV roleIV null;try {// 获取数据库连接conn dataSource.getConnection();// 构造查询SQL语句String sql select id, role_name, note from t_role where id ?;ps conn.prepareStatement(sql);// 设置查询参数ps.setLong(1, id);// 执行查询rs ps.executeQuery();// 处理查询结果while (rs.next()) {// 构建角色对象roleIV new RoleIV();roleIV.setId(rs.getLong(id));roleIV.setRoleName(rs.getString(role_name));roleIV.setNote(rs.getString(note));}} catch (SQLException e) {e.printStackTrace();} finally {// 关闭数据库连接及相关资源if (conn ! null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}return roleIV;}
}然后再执行测试代码 /*** 测试作用域* 该方法通过创建一个注解配置的应用上下文并从该上下文中获取RoleDataSourceService类型的bean实例* 以此来验证Spring容器中bean的作用域特性。* 该方法不接受参数且没有返回值。*/public static void testScope() {// 创建一个注解配置的应用上下文并指定配置类ApplicationConfigAnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(ApplicationConfig.class);// 从上下文中获取RoleDataSourceService类型的bean实例RoleDataSourceService service1 context.getBean(RoleDataSourceService.class);RoleDataSourceService service2 context.getBean(RoleDataSourceService.class);// 打印两个bean实例是否为同一个对象的引用System.out.println(service1 service2);// 关闭应用上下文context.close();}就会返回false了也就是当我们从SpringIoC容器中获取对象时获取的都是新的实例不同的对象
在互联网应用中Spring会使用实现了WebApplicationContext接口(该接口继承了ApplicationContext)的实现类作为IoC容器此容器有两种常见的作用域(会话和请求)会话(Session)在Web应用中使用在会话过程中只创建一个实例请求(request)在Web应用中使用在一次请求中Spring会创建一个实例该配置可以在Java代码中进行也可以在XML配置文件中进行如下所示
import org.springframework.web.context.WebApplicationContext;/*** 实现RoleDataSourceService接口提供获取角色信息的功能*/
Service
/*** 为Spring Web应用程序定义会话级别的作用域。* 使用此注解的bean将被存储在HTTP会话中意味着它们在同一个会话内的所有HTTP请求中都是可用的。* 这是针对需要在多个相关请求之间共享状态的情况而设计的例如用户的登录状态。** 注意此注解仅适用于Web应用程序上下文。*/
Scope(WebApplicationContext.SCOPE_SESSION)public class RoleDataSourceServiceImpl implements RoleDataSourceService {// 通过自动装配获取数据源AutowiredDataSource dataSource null;!-- 定义一个RoleII类型的bean实例id为2角色名为高级工程师备注为重要人员 --bean idrole2 classcom.ssm.pojo.RoleII scopeprototype!-- 设置角色id为2 --property nameid value2 /!-- 设置角色名为高级工程师 --property nameroleName value高级工程师 /!-- 设置备注为重要人员 --property namenote value重要人员 //bean使用Spring表达式
Spring还提供了更灵活的方式那就是Spring表达式(Spring EL), 它远比其他注入方式更强大其大致内容如下 Spring Expression Language (Spring EL) 是Spring框架中的一种表达式语言它主要用于在运行时查询和操作对象图。Spring EL设计的目标是简化数据绑定和表达式评估特别是在Spring应用程序上下文中。以下是一些关于Spring EL的关键点 对象导航Spring EL允许你通过点号.来导航对象的属性例如 person.name 获取 person 对象的 name 属性。方法调用支持调用对象的方法例如 list.sort() 或 date.format(‘yyyy-MM-dd’)。集合操作可以索引和遍历集合如 list[0] 或 list[0…2] 选取前三个元素。条件和逻辑运算支持 if、and、or、not 等逻辑运算符以及比较运算符、、 等。算术运算支持基本的数学运算如加减乘除 (, -, *, /) 和取余数 (%)。类型转换可以显式地转换类型例如 (int)number。变量和参数在表达式中可以引用变量和方法参数。上下文访问可以访问Spring容器中的bean例如 myBean 引用名为 myBean 的bean。表达式结果表达式的结果可以是任何Java类型包括null。** spel:expression**在XML配置中使用 #{} 语法来包含Spring EL表达式例如property namesomeProperty value#{myBean.someMethod()} /。 Spring EL相关的类 /*** 测试表达式解析功能。* 该方法演示了如何使用Spring表达式语言SpEL解析一个简单的字符串表达式并获取其值。**/public static void testExpression(){// 创建SpEL表达式解析器ExpressionParser parser new SpelExpressionParser();// 解析一个字符串表达式并输出其值Expression expression parser.parseExpression(Hello World);String str (String) expression.getValue();System.out.println(str);// 解析并执行字符串的charAt方法输出第一个字符expression parser.parseExpression(Hello World.charAt(0));char ch (char) expression.getValue();System.out.println(ch);// 解析字符串并获取其字节表示expression parser.parseExpression(Hello World.bytes);byte[] bytes (byte[]) expression.getValue();System.out.println(bytes);// 获取字符串的字节长度expression parser.parseExpression(Hello World.bytes.length);int length (int) expression.getValue();System.out.println(length);// 使用SpEL构造一个新的字符串对象expression parser.parseExpression(new String(abc));String str2 (String) expression.getValue();System.out.println(str2);}如代码所示是一个简单的使用Spring EL的例子, 通过表达式可以创建对象、调用对象的方法或者获取属性
SpringEL还支持变量的解析使用变量解析时常常用到一个接口EvaluationContext,可以有效解析表达式中的变量它有一个实现类StandardEvaluationContext用变量解析表达式会使表达式更加灵活在针对SpringIoC容器进行解析的时候可以直接获取配置的属性值但使用这些表达式会降低可读性不适合处理复杂的问题 /*** 测试SpEL表达式的求值功能。* 该方法首先演示了如何使用SpEL获取和设置对象属性的值接着展示了如何对列表元素进行操作。*/public static void testEvaluation() {// 创建SpelExpressionParser实例以解析表达式ExpressionParser parser new SpelExpressionParser();// 创建RoleIV实例用于在表达式求值时提供属性值RoleIV roleIV new RoleIV(1L, admin, administrator);// 解析表达式以获取note属性的值Expression expression parser.parseExpression(note);String note (String) expression.getValue(roleIV);System.out.println(note);// 创建表达式求值的上下文环境并将roleIV设置为上下文中的根对象EvaluationContext ctx new StandardEvaluationContext(roleIV);// 设置note属性的新值为new_administratorparser.parseExpression(note).setValue(ctx, new_administrator);// 获取更新后的note属性值note parser.parseExpression(note).getValue(ctx, String.class);System.out.println(note);// 获取RoleIV实例的getRoleName()方法返回值String roleName parser.parseExpression(getRoleName()).getValue(ctx, String.class);System.out.println(roleName);// 初始化并创建一个字符串列表用于演示如何在SpEL中操作集合ListString list new ArrayListString();list.add(value1);list.add(value2);// 将列表绑定到上下文中以在表达式中使用ctx.setVariable(list, list);// 更新列表的第一个元素的值为new_value1parser.parseExpression(#list[0]).setValue(ctx, new_value1);// 获取更新后的列表的第一个元素的值System.out.println(parser.parseExpression(#list[0]).getValue(ctx));}EvaluationContext使用了它的实现类StandardEvaluationContext进行实例化在构造方法中将角色对象传递给它它就会基于这个类进行解析
Bean的属性和方法
使用注解的方式需要用到Value,在属性文件中的读取需要$,在Spring EL中需要#在角色类中使用使用Spring EL初始化如下代码所示
package com.ssm.pojo;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** ELRole类用于演示通过Spring的注解方式注入属性值。* 该类被标记为Component表示它是一个Spring组件可以被其他组件依赖。* 其中使用的Value注解用于注入具体的属性值。*/
Component(elRole)
public class ELRole {// 使用Value注解注入id的值此处使用SpEL表达式的方式注入示例值为1。Value(#{1})private Long id;// 使用Value注解注入roleName的值此处使用字符串方式注入。Value(#{role_name_1})private String roleName;// 使用Value注解注入note的值此处使用字符串方式注入。Value(#{note_1})private String note;/*** 获取角色ID* return 返回角色的ID值*/public Long getId() {return id;}/*** 设置角色ID* param id 角色的ID值*/public void setId(Long id) {this.id id;}/*** 获取角色名称* return 返回角色名称字符串*/public String getRoleName() {return roleName;}/*** 设置角色名称* param roleName 角色名称字符串*/public void setRoleName(String roleName) {this.roleName roleName;}/*** 获取角色备注信息* return 返回角色的备注信息字符串*/public String getNote() {return note;}/*** 设置角色备注信息* param note 角色的备注信息字符串*/public void setNote(String note) {this.note note;}}
这样就定义了一个BeanName为elRole的角色类同时给它所有的属性都进行了初始化然后可以通过另一个Bean引用它的属性或者调用它的方法如下代码所示
package com.ssm.pojo;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** ELBean类用于演示和展示Spring Expression Language (EL)的使用。* 通过注解Value从Spring表达式语言中获取值并注入到类的属性中。*/
Component(elBean)
public class ELBean {// 从Java Math类中获取PI值Value(#{T(java.lang.Math).PI})private double pi;// 获取一个随机数Value(#{T(Math).random()})private double random;// 根据elRole的id值加1赋值给numValue(#{elRole.id1})private int num;// 将elRole的roleName和note拼接成一个字符串Value(#{elRole.roleName elRole.note})private String str;// 判断elRole的id是否等于1Value(#{elRole.id 1})private boolean equalNum;// 判断elRole的note是否等于note_1Value(#{elRole.note eq note_1})private boolean eqaulString;// 判断elRole的id是否大于2Value(#{elRole.id 2})private boolean greater;// 判断elRole的id是否小于2Value(#{elRole.id 2})private boolean less;// 根据条件判断选择赋值5或1给maxValue(#{elRole.id 1 ? 5 : 1})private int max;// 如果elRole的note存在则赋值note否则赋值helloValue(#{elRole.note?: hello})private String defaultString;// 通过beanName获取ELRole类型的bean并注入Value(#{elRole})private ELRole elRole;// 获取ELRole的id属性Value(#{elRole.id})private Long id;// 调用ELRole的getNote方法获取note属性的值Value(#{elRole.getNote().toString()})private String note;/*** 获取ELRole对象* return 返回ELRole对象*/public ELRole getElRole() {return elRole;}/*** 设置ELRole对象* param elRole 要设置的ELRole对象*/public void setElRole(ELRole elRole) {this.elRole elRole;}/*** 获取实体的ID* return 返回实体的ID值*/public Long getId() {return id;}/*** 获取圆周率π的值* return 返回圆周率π的近似值*/public double getPi() {return pi;}/*** 设置圆周率π的值* param pi 要设置的圆周率π的近似值*/public void setPi(double pi) {this.pi pi;}/*** 获取一个随机数* return 返回一个随机数值*/public double getRandom() {return random;}/*** 设置一个随机数* param random 要设置的随机数值*/public void setRandom(double random) {this.random random;}/*** 设置实体的ID* param id 要设置的实体ID*/public void setId(Long id) {this.id id;}/*** 获取备注信息* return 返回备注信息字符串*/public String getNote() {return note;}/*** 设置备注信息* param note 要设置的备注信息字符串*/public void setNote(String note) {this.note note;}
}
可以通过BeanName注入也可以通过OGNL获取其属性或者调用其方法注入其他的Bean注意表达式#{elRole.getNote().toString()}的注入getNote()方法可能返回null,然后导致toString()抛异常可以写成#{elRole.getNote()?.toString()},表达式中的问号事先判断是否非空如果不是非空则不再调用toString方法
使用类的静态常量和方法 // 从Java Math类中获取PI值Value(#{T(java.lang.Math).PI})private double pi;// 获取一个随机数Value(#{T(Math).random()})private double random;Math是java.lang.*包下的Math类在Java代码中使用该类不需要import对于Spring EL也是也可以像代码所示给全限定名