1、依赖注入的三种方式
- 构造器注入
- setter注入
- 接口注入
构造器注入和setter注入是最主要的方式
1.1 构造器注入
package com.learn.ssm.pojo;
import java.io.Serializable;
public class Role implements Serializable {
private int id;
private String roleName;
private String note;
public Role(String roleName, String note){
this.roleName = roleName;
this.note = note;
}
public int getId() {
return id;
}
public void setId(int 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;
}
}
这个时候是没有办法利用无参数构造方法去创建对象的,为了使Spring能够创建对象,可以依造如下代码:
<bean id="role1" class="com.learn.ssm.pojo.Role">
<constructor-arg name="roleName" value="总经理"/>
<constructor-arg name="note" value="公司管理者"/>
</bean>
<!-------------------------另外一种---------------------------- -->
<bean id="role1" class="com.learn.ssm.pojo.Role">
<constructor-arg index="0" value="总经理"/>
<constructor-arg index="1" value="公司管理者"/>
</bean>
1.2使用setter注入
setter注入是Spring中最主流的注入方式,他利用Java Bean规范所定义的setter 方法来完成注入,灵活且可读性高。它消除了使用构造器注入时出现多个参数的可能性,首先把构造方法声明为无参数的,然后使用setter注入为其设置对应的值。
<bean id="role2" class="com.learn.ssm.pojo.Role">
<property name="roleName" value="高级工程师"/>
<property name="note" value="重要人员"/>
</bean>
这样,Spring就会通过反射调用没有参数的构造方法生成对象,同时通过反射对应的setter注入配置的值了。这种方式为最主要的方式
1.3 接口注入
有些时候资源并非来自于自身系统,而是来自外界,比如数据库连接资源完全可以在Tomcat下配置,然后通过JNDI的形式去获取他。
打开服务器的context.xml文件:
在context文件中加入自己的一个资源:
<?xml version="1.0" encoding="UTF-8" ?>
<Context>
<!--
name 为JNDI名称
url 是数据库的jdbc连接
username 用户名
password 数据库密码 -->
<Resource name="jdbc/ssm"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/ssm"
username="root"
password="123456"/>
</Context>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/ssm</value>
</property>
</bean>
2、装配Bean概述
Spring 中提供了3种方法进行配置
-
在xml中显示配置
-
在java的接口和类中实现配置
-
隐式Bean的发现机制和自动装配原则
使用哪种方式:
- 基于约定优先配置的原则,最优先的应该是通过隐式Bean的发现机制和自动装配的原则。这样的好处是减少程序开发者的决定权,简单而不失灵活
- 在没有办法使用自动装配原则的情况下应该优先考虑Java接口和类中实现配置,这样的好处是避免xml配置的泛滥,也更为容易。比如一个父类有多个子类,比如学生类有两个子类,男学生类和女学生类,通过Ioc容器初始化一个学生类,容器将无法知道使用哪个子类去初始化,这个是以后可以使用Java的注解配置去指定
- 在上述方法都无法使用的情况下,那么只能选择xml去配置SpringIoc容器。由于现实工作中常常用到第三方库,有些类并不是我们开发的,所有只好通过xml配置
3、通过xml装配Bean
引入xml模式:
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
3.1 装配简单值
<bean id="role2" class="com.learn.ssm.pojo.Role">
<property name="note" value="重要人员"/>
<property name="roleName" value="高级工程师"/>
<property name="id" value="1"/>
</bean>
- id属性是Spring找到这个Bean的编号,不过id不是一个必需的属性,如果没有id,那么Spring会操用‘全限定名#{number}’的格式生成编号。如果只声明一个这样的类,而没有声明id=“role2”,那么Spring为其生成的编号就是"com.learn.ssm.pojo.Role#0"
- class 全限定名
- property name是属性名称,value是值
- 如果需要注入自定义的类,将value替换ref
3.2 装配集合
先定义一个bean:
package com.learn.ssm.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class ComplexAssembly {
private Long id;
private List<String> list;
private Map<String, String> map;
private Properties props;
private Set<String> set;
private String[] array;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public String[] getArray() {
return array;
}
public void setArray(String[] array) {
this.array = array;
}
}
<bean id="complexAssembly" class="com.learn.ssm.pojo.ComplexAssembly">
<property name="id" value="1"/>
<property name="list">
<list>
<value>value-list-1</value>
<value>value-list-2</value>
<value>value-list-3</value>
</list>
</property>
<property name="map">
<map>
<entry key="key1" value="value-key-1"/>
<entry key="key2" value="value-key-2"/>
<entry key="key3" value="value-key-3"/>
</map>
</property>
<property name="props">
<props>
<prop key="prop1">value-prop1</prop>
<prop key="prop2">value-prop2</prop>
<prop key="prop3">value-prop3</prop>
</props>
</property>
<property name="set">
<set>
<value>value-set-1</value>
<value>value-set-2</value>
<value>value-set-3</value>
</set>
</property>
<property name="array">
<array>
<value>value-array-1</value>
<value>value-array-2</value>
<value>value-array-3</value>
</array>
</property>
</bean>
有时候需要更加复杂的装载,比如一个List可以是一个系列类的对象,又如一个Map集合类,键可以是一个类对象,而值也要是一个类对象,这些也是Java中常常可以看到的,可以先建立两个pojo:
package com.learn.ssm.pojo;
public class Role {
private int id;
private String roleName;
private String note;
public Role(){}
public Role(String roleName, String note){
this.roleName = roleName;
this.note = note;
}
public int getId() {
return id;
}
public void setId(int 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;
}
}
public class User{
private Long id;
private String userName;
private String note;
/** getter and setter */
}
再建立一个复杂的POJO:
package com.learn.ssm.pojo;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class UserRoleAssembly {
private Long id;
private List<Role> list;
private Map<Role, User> map;
private Set<Role> set;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Role> getList() {
return list;
}
public void setList(List<Role> list) {
this.list = list;
}
public Map<Role, User> getMap() {
return map;
}
public void setMap(Map<Role, User> map) {
this.map = map;
}
public Set<Role> getSet() {
return set;
}
public void setSet(Set<Role> set) {
this.set = set;
}
}
<bean id="role1" class="com.learn.ssm.pojo.Role">
<property name="id" value="1"/>
<property name="roleName" value="role_name_1"/>
<property name="note" value="role_note_1"/>
</bean>
<bean id="role2" class="com.learn.ssm.pojo.Role">
<property name="id" value="2"/>
<property name="roleName" value="role_name_2"/>
<property name="note" value="role_note_2"/>
</bean>
<bean id="user1" class="com.learn.ssm.pojo.User">
<property name="id" value="1"/>
<property name="userName" value="user_name_1"/>
<property name="note" value="role_note_1"/>
</bean>
<bean id="user2" class="com.learn.ssm.pojo.User">
<property name="id" value="2"/>
<property name="userName" value="user_name_2"/>
<property name="note" value="role_note_1"/>
</bean>
<bean id="userRoleAssembly" class="com.learn.ssm.pojo.UserRoleAssembly">
<property name="id" value="1"/>
<property name="list">
<list>
<ref bean="role1"/>
<ref bean="role2"/>
</list>
</property>
<property name="map">
<map>
<entry key-ref="role1" value-ref="user1"/>
<entry key-ref="role2" value-ref="user2"/>
</map>
</property>
<property name="set">
<set>
<ref bean="role1"/>
<ref bean="role2"/>
</set>
</property>
</bean>
这样就完成了装配。
3.3 命名空间装配
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="role1" class="com.learn.ssm.pojo.Role" c:_0="1"
c:_1="role_name_1" c:_2="role_note_1"/>
<bean id="role1" class="com.learn.ssm.pojo.Role" p:_0="2"
p:_1="role_name_1" p:_2="role_note_2"/>
</beans>
4、通过注解装配
4.1 使用@Component装配Bean
先定义一个POJO:
package com.learn.ssm.chapter10.annotation.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value="role")
public class Role {
@Value("1")
private Long id;
@Value("role_name_1")
private String roleName;
@Value("role_note_1")
private String note;
public Long getId() {
return id;
}
public 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;
}
}
- 注解@Component代表Spring IoC会把这个类扫描生成Bean实例,而其中的value属性代表这个类在Spring的id,这就相当于xml方式定义的Bean的id,也可以简写成@Component(“role”),如果不写,Spring Ioc默认以类名但是第一个字母变成小写作为id
- 注解@Value 代表的是值的注入,这里只是简单注入一些值,其中id是一个long型,注入的时候Spring会转换类型
用一个Java Config来告诉Spring Ioc去扫描这个对象:
package com.learn.ssm.chapter10.annotation.pojo;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
public class PojoConfig {
}
- 包名和代码清单Role的POJO保持一致
- @ComponentScan代表进行扫描,默认是扫描当前包
现在可以通过Spring Ioc容器的实现类 —— AnnotationConfigApplicationContext去生成Ioc容器了
package com.learn.ssm.chapter10.annotation;
import com.learn.ssm.chapter10.annotation.pojo.PojoConfig;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AnnotationMain {
public static void main(String[] args){
ApplicationContext context = new AnnotationConfigApplicationContext(PojoConfig.class);
Role role = (Role) context.getBean("role");
System.out.println(role.getId());
}
}
存在两个弊端:
-
对于@ComponentScan注解,只扫描了所在包
-
没有注入对象
@ComponentScan存在两个配置项,第1个是basePackages,可以配置一个Java包的数据,Spring会根据它的配置扫描对应的包和子包;第2个是basePackageClasses,可以配置多个类,Spring会根据配置到额类所在的包,扫描包和子包定义一个接口RoleService:
package com.learn.ssm.chapter10.annotation.service;
import com.learn.ssm.chapter10.annotation.pojo.Role;
public interface RoleService {
public void printRoleInfo(Role role);
}
使用接口可以将定义和实现分离,这样更加灵活.
package com.learn.ssm.chapter10.annotation.service.imp1;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.RoleService;
import org.springframework.stereotype.Component;
@Component
public class RoleServiceImp1 implements RoleService {
@Override
public void printRoleInfo(Role role) {
System.out.println("id = " + role.getId());
System.out.println("roleName = " + role.getRoleName());
System.out.println("note = " + role.getNote());
}
}
这里的@Component表明它是一个Spring所需要的Bean,而且也实现了对应的RoleService接口所定义的printRoleInfo方法。
package com.learn.ssm.chapter10.annotation.config;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.imp1.RoleServiceImp1;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackageClasses = {Role.class, RoleServiceImp1.class})
//@ComponentScan(basePackages = {"com.learn.ssm.chapter10.annotation.pojo","com.learn.ssm.chapter10.annotation.service"})
//@ComponentScan(basePackages = {"com.learn.ssm.chapter10.annotation.pojo","com.learn.ssm.chapter10.annotation.service"},
//basePackageClasses = {Role.class,RoleServiceImp1.class})
public class ApplicationConfig {
}
- 这是对扫描包的定义,可以采用任意一个@ConponentScan去定义
- 如果采用多个@ComponentScan去定义对应的包,但是每定义一个@ComponentScan,Spring就会为所定义的类生成一个新的对象,也就是所配置的Bean将会生成多个实例
- 对于已定义的basePackages和basePackageClasses的@ComponentScan,Spring会进行专门的区分,也就是说在同一个@CompinentScan中即使重复定义相同的包或者子包,也不会造成一个Bean多次扫描
package com.learn.ssm.chapter10.annotation;
import com.learn.ssm.chapter10.annotation.config.ApplicationConfig;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.RoleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AnnotationMain {
public static void main(String[] args){
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Role role = (Role) context.getBean(Role.class);
RoleService roleService = context.getBean(RoleService.class);
roleService.printRoleInfo(role);
((AnnotationConfigApplicationContext) context).close();
}
}
4.2自动装配
Spring自己发现对应的Bean,自动完成装配工作,应用@Autowored
package com.learn.ssm.chapter10.annotation.service;
public interface RoleService2 {
public void printRoleInfo();
}
package com.learn.ssm.chapter10.annotation.service.impl;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.RoleService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("RoleService2")
public class RoleServiceImpl2 implements RoleService2 {
@Autowired
private Role role = null;
@Override
public void printRoleInfo() {
System.out.println("id = " + role.getId());
System.out.println("roleName = " + role.getRoleName());
System.out.println("note = " + role.getNote());
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
这里@Autowired注解,表示在Spring Ioc定位所有的Bean后,这个字段需要按类型注入,这样IoC容器就会寻找资源,然后将其注入。如果认为一个属性是可有可无,这个时候可以荣国@Autowired(required=false)
@Autowired也可以写在方法上
4.3 自动装配的歧义性(@Primary和@Qualifie)
如果定义一个接口,有多个实现类,比如下面:
package com.learn.ssm.chapter10.annotation.service.impl;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.RoleService;
import org.springframework.stereotype.Component;
@Component("roleService3")
public class RoleServiceImpl3 implements RoleService {
@Override
public void printRoleInfo(Role role) {
System.out.println("{id = " + role.getId());
System.out.println("roleName = " + role.getRoleName());
System.out.println("note = " + role.getNote() + "}");
}
}
再新建一个RoleController类:
package com.learn.ssm.chapter10.annotation.controller;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
public class RoleController {
@Autowired
private RoleService roleService = null;
public void printRole(Role role){
roleService.printRoleInfo(role);
}
}
这里的字段roleService是一个RoleService接口类型,RoleService有两个实现类,只是后Srping Ioc容器就会无法判断将哪个对象注入进来,于是就会抛出异常
通过上面的分析,可以知道产生这样的状况是因为它采用的是按类型来注入对象,而在java接口可以有多个实现类,同样的抽象类也可能有多个实例化的类,这样就会造成通过类型获取Bean不唯一。可以回想到Spring Ioc最底层的容器接口 —— BeanFactory的定义,存在一个通过类型获取Bean的方法:
<T> T getBean(Class<T> requiredType) throws BeansException;
为了消除歧义性,Spring提供了两个注解@Primary 和@Qualifier
4.3.1 注解@Primary
注解@Primary告诉Spring Ioc容器,优先使用该类注入:
package com.learn.ssm.chapter10.annotation.service.impl;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.RoleService;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component("roleService3")
@Primary
public class RoleServiceImpl3 implements RoleService {
@Override
public void printRoleInfo(Role role) {
System.out.println("{id = " + role.getId());
System.out.println("roleName = " + role.getRoleName());
System.out.println("note = " + role.getNote() + "}");
}
}
这里的@Primary就会告诉Spring Ioc容器,如果存在多个RoleService类型,无法判断注入哪个的时候,优先讲RoleServiceImpl的实例注入,但是@Primary只能解决首要性的问题,不能解决选择性的问题
4.3.2 注解@Qualifier
@Qualifire是一个可以按照名称寻找的注解
package com.learn.ssm.chapter10.annotation.controller;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class RoleController {
@Autowired
@Qualifier("roleService3")
private RoleService roleService = null;
public void printRole(Role role){
roleService.printRoleInfo(role);
}
}
这个时候就会通过所定义的类型注入
4.4 装载带有参数的构造方法类
增加RoleController如下:
package com.learn.ssm.chapter10.annotation.controller;
import com.learn.ssm.chapter10.annotation.service.RoleService;
public class RoleController2 {
private RoleService roleService = null;
public RoleController2(RoleService roleService){
this.roleService = roleService;
}
}
package com.learn.ssm.chapter10.annotation.controller;
import com.learn.ssm.chapter10.annotation.service.RoleService;
import org.springframework.beans.factory.annotation.Qualifier;
public class RoleController2 {
private RoleService roleService = null;
public RoleController2(@Qualifier RoleService roleService){
this.roleService = roleService;
}
}
4.5使用@Bean装配Bean
以上都是通过@Component装配Bean,但是@Component只能注解在类上,不能注解到方法上。如果引入第三方包的话,就会有问题。
@Bean可以注解到方法上,并且将方法返回对象作为Spring的Bean
@Bean(name="dataSource")
public DataSource getDataSource(){
Properties properties = new Properties();
properties.setProperty("driver","...");
properties.setProperty("url","...");
properties.setProperty("username","...");
properties.setProperty("password","...");
DataSource dataSource = null;
try{
dataSource = BasicDataSourceFactory.createDataSource(properties);
}catch (Exception e){
e.printStackTrace();
}
return dataSource;
}
5、使用Profile
带有@Profile的数据源
package com.learn.ssm.chapter10.profile;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.Properties;
@Component
public class ProfileDataSource {
@Bean(name="devDataSource")
@Profile("dev")
public DataSource getDevDataSource(){
Properties props = new Properties();
props.setProperty("driver","com.mysql.cj.jdbc.Driver");
props.setProperty("url","jdbc:mysql://loaclhost:3306/chapter12");
props.setProperty("username","root");
props.setProperty("password","*****");
DataSource dataSource = null;
try{
dataSource = BasicDataSourceFactory.createDataSource(props);
}catch (Exception e){
e.printStackTrace();
}
return dataSource;
}
@Bean(name="testDataSource")
@Profile("test")
public DataSource getTestDataSource(){
Properties props = new Properties();
props.setProperty("driver","com.mysql.cj.jdbc.Driver");
props.setProperty("url","jdbc:mysql://loaclhost:3306/chapter13");
props.setProperty("username","root");
props.setProperty("password","*****");
DataSource dataSource = null;
try{
dataSource = BasicDataSourceFactory.createDataSource(props);
}catch (Exception e){
e.printStackTrace();
}
return dataSource;
}
}
使用xml配置数据源:
配置一个:
配置多个:
6、加载属性(properties)文件
在开发的过程中,配置文件往往就是那些(properties)文件,比如使用properties文件配置数据库文件,又如database-config.properties,代码如下:
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=***
package com.learn.ssm.chapter10.annotation.config;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import com.learn.ssm.chapter10.annotation.service.impl.RoleServiceImp1;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
@ComponentScan(basePackageClasses = {Role.class, RoleServiceImp1.class})
//@ComponentScan(basePackages = {"com.learn.ssm.chapter10.annotation.pojo","com.learn.ssm.chapter10.annotation.service"})
//@ComponentScan(basePackages = {"com.learn.ssm.chapter10.annotation.pojo","com.learn.ssm.chapter10.annotation.service"},
//basePackageClasses = {Role.class,RoleServiceImp1.class})
@PropertySource(value={"classpath:./database-config.properties"},ignoreResourceNotFound = false)
public class ApplicationConfig {
}
package com.learn.ssm.chapter10.annotation.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@Configuration
@ComponentScan(basePackages = {"com.learn.ssm.chapter10.annotation"})
@PropertySource(value = {"classpath:./database-config.properties"},ignoreResourceNotFound = true)
public class ApplicationConfig {
@Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
package com.learn.ssm.chapter10.annotation.config;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
import java.util.Properties;
public class DataSourceBean {
@Value("${jdbc.database.driver}")
private String driver = null;
@Value("${jdbc.database.url}")
private String url = null;
@Value("${jdbc.database.username}")
private String username = null;
@Value("${jdbc.database.password}")
private String password = null;
@Bean(name = "dataSource")
public DataSource getDataSource(){
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;
}
}
6.2 使用xml加载文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.learn.ssm.chapter10.annotation"/>
<context:property-placeholder ignore-resource-not-found="true" location="classpath:database-config.properties"/>
</beans>
配置多个属性文件:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<array>
<value>classpath:database-config.properties</value>
<value>classpath:log4j.properties</value>
</array>
</property>
<property name="ignoreResourceNotFound" value="true"/>
</bean>
7、条件化装配Bean
在某些条件下不需要去装配Bean,比如当没有database-config.properties属性配置的时候,就不要去创建数据源,这个时候,就要通过条件化去配置。Spring提供了@Conditional去配置,通过它可以配置一个或者多个类,只是这些类都需要实现接口Condition
@Bean(name = "dataSource")
@Conditional({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;
}
这里代码通过@Value往参数里注入了对应属性文件的配置,但是我们没有办法确定这些数据源连接池的属性是否在属性文件已经配置完整,如果不充足的属性配置,则会创建失败,为此需要判断属性文件的配置是否充足才能继续创建Bean,通过@Conditional去引入一个类——DataSourceCondition,由它来进行判断。
package com.learn.ssm.chapter10.annotation.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;
public class DataSourceCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment env = conditionContext.getEnvironment();
return env.containsProperty("jdbc.database.driver")
&& env.containsProperty("jdbc.database.url")
&& env.containsProperty("jdbc.database.username")
&& env.containsProperty("jdbc.database.password");
}
}
通过返回的true和false,来进行判断是否创建Bean.
8、Bean的作用域
在默认的情况下,SpringIoc只会创建一个Bean实例,比如下面的测试:
package com.learn.ssm.chapter10.annotation;
import com.learn.ssm.chapter10.annotation.config.ApplicationConfig;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AnnotationMain {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Role role1 = (Role) context.getBean("role");
Role role2 = (Role) context.getBean("role");
System.out.println(role1 == role2);
}
}
断点调试:
可以知道,两个对象是一样的
默认情况下,Spring Ioc容器只生成一个实例,但是有时候我们想要生成多个实例,这些是由Spring作用域决定的:
- 单例(singleton):它是默认的选项,在整个应用中,Spring只为其生成一个Bean实例
- 原型(prototype):当每次注入,或者通过Spring IoC容器获取Bean时,Spring都会为它创建一个新实例
- 会话(session):在Web应用中使用,就是在会话过程中Spring只创建一个实例。
- 请求(request):在Web应用中使用,一次请求创建一个实例,不同的请求会创建不同实例
修改Role:
package com.learn.ssm.chapter10.annotation.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component(value="role")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Role {
@Value("1")
private Long id;
@Value("role_name_1")
private String roleName;
@Value("role_note_1")
private String note;
public Long getId() {
return id;
}
public 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;
}
@Bean(name = "newRole")
public Role getRole(){
return new Role();
}
}
注明为原型,就会生成不同的实例。
10、使用Spring表达式(Spring EL)
SpringEL远远比注入方式强大,我们需要学习它
SpringEL有很多功能:
- 使用Bean的id来引用Bean
- 调用指定对象的方法和访问对象的属性
- 进行运算
- 提供正则表达式进行匹配
- 集合配置
10.1、EL相关的类
package com.learn.ssm.chapter10;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class EL {
public static void main(String[] args){
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'hello world'");
String str = (String)exp.getValue();
System.out.println(str);
//通过el访问普通方法
exp = parser.parseExpression("'hello world'.charAt(0)");
char ch = (Character) exp.getValue();
System.out.println(ch);
//通过el访问getter方法
exp = parser.parseExpression("'hello world'.bytes");
byte[] bytes = (byte[]) exp.getValue();
System.out.println(bytes);
//通过el访问属性,相当与"hello world".getBytes().length
exp = parser.parseExpression("'hello world'.bytes.length");
int lenght = (Integer) exp.getValue();
System.out.println(lenght);
exp = parser.parseExpression("new String('abc')");
String abc = (String) exp.getValue();
System.out.println(abc);
}
}
package com.learn.ssm.chapter10;
import com.learn.ssm.chapter10.annotation.pojo.Role;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.ArrayList;
import java.util.List;
public class EL {
public static void main(String[] args){
Role role = new Role(1L,"role_name","note");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = null;
exp = parser.parseExpression("note");
//相当于从role中获取备注信息
String note = (String) exp.getValue(role);
System.out.println(note);
//变量环境类,并且将角色对象role作为其根结点
EvaluationContext context = new StandardEvaluationContext(role);
//获取备注,这里的String.class指明,我们希望返回的是一个字符串
note = parser.parseExpression("note").getValue(context, String.class);
System.out.println(note);
String roleName = parser.parseExpression("getRoleName()").getValue(context,String.class);
System.out.println(roleName);
//新增环境变量
List<String> list = new ArrayList<>();
list.add("value1");
list.add("value2");
//给变量环境增加变量
context.setVariable("list",list);
parser.parseExpression("#list[1]").setValue(context,"update_value2");
System.out.println(parser.parseExpression("#list[1]").getValue(context));
}
}
10.2、Bean属性和方法
使用注解的方式需要用到注解@Value,在属性文件的读取中使用的是"$",而在Spring EL则使用“#”。下面以角色类为例子进行说明:
**使用SpringEL初始化角色类
package com.learn.ssm.chapter10.el.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component("role")
public class Role {
@Value("#{1}")
private Long id;
@Value("#{'role_name_1'")
private String roleName;
@Value("@{'note_1'}")
private String note;
public Long getId() {
return id;
}
public 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;
}
}
这样就可以定义一个BeanName为role的角色类了,同时给予它所有的属性赋值,这个时候可以通过另外一个Bean去引用它的属性或者调用它的方法,比如新建一个类-ELBean作为测试:
package com.learn.ssm.chapter10.el.pojo;
import org.springframework.beans.factory.annotation.Value;
public class ElBean {
@Value("#{role}")
private Role role;
@Value("#{role.id}")
private Long id;
@Value("#{role.getNote().toString()}")
private String note;
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
我们可以通过Beanname进行注入,也可以通过OGNL获取其属性或者调用其方法来注入其他的Bean中。注意,表达式“#{role.getNote().toString()}”的注入,因为getNote可能返回为null,这样toString()方法就会抛出异常,可以加个?判断是否非null,例如:
#{role.getNote()?.toString()}
10.3 使用类的静态常量和方法
@Value("#{T(Math).PI}")
private double pi;
如果没有import,需要给出全限定名,使用静态方法类似;
10.4 SpringEL运算
@Value("#{role.id + 1}")
private int num;
连接字符串:
@Value("#{role.roleName + role.note}")
private String str;
比较两个值是否相等,比如角色是否为1,角色名称是否为"role_name_001",例如:
@Value("#{role.id == 1}")
private boolean equalNum;
@Value("#{role.note eq 'note_1'}")
private boolean equalString;
@Value("#{role.id > 2}")
private boolean greater;
@Value("#{role.id < 2}")
private boolean less;
@Value("#{role.id > 1 ? 5 : 1 }")
private int max;
@Value("#{role.note?: 'hello'}")
private String