Spring 能帮我们做什么
①.Spring 能帮我们根据配置文件创建及组装对象之间的依赖关系。
②.Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制。
③.Spring 能非常简单的帮我们管理数据库事务。
④.Spring 还提供了与第三方数据访问框架(如Hibernate、JPA)无缝集成,而且自己也提供了一套JDBC访问模板来方便数据库访问。
⑤.Spring 还提供与第三方Web(如Struts1/2、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建。
⑥.Spring 能方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架)。
Spring 的优势
低侵入 / 低耦合 (降低组件之间的耦合度,实现软件各层之间的解耦)
声明式事务管理(基于切面和惯例)
方便集成其他框架(如MyBatis、Hibernate)
降低 Java 开发难度
Spring 框架中包括了 J2EE 三层的每一层的解决方案(一站式)
Spring IoC 是什么
IoC:Inverse of Control(控制反转)
读作“反转控制”更好理解。将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
正控:若要使用某个对象,需要自己去负责对象的创建
反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
●谁控制谁?
我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;
●控制什么?
主要控制了外部资源获取。
●为何是反转?
有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;
为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;
●反转了什么?
依赖对象的获取被反转了。
案例1:认识springIOC
正控
1.创建员工类
Employee.java
public classEmployee {/**系统id*/
private intid;/**员工编号*/
privateString employeeNo;/**员工姓名*/
privateString employeeName;/**员工性别*/
privateString sex;/**出生日期*/
privateDate birthDay;/**部门编号*/
privateString officeNo;/**岗位编号*/
privateString postNo;/**入职时间*/
privateDate entryTime;/**特长*/
privateString speciality;/**兴趣爱好*/
privateString hobby;/**setter and getter*/}
2.创建测试方法,并调用构造函数创建对象。
TestSpringEmp.java
public classTestSpringEmp {public static voidmain(String[] args) {
Employee emp= newEmployee();
System.out.println(emp);
}
}
springIOC 控制反转
环境搭建:
1.idea 工程添加Spring相关jar包。【libs 上传至git 地址,操作方法见附录】
2.创建配置文件,可以自定义文件名spring.xml。
3.调用API。
程序思路:
1.在spring.xml中配置bean标签,IOC容器通过加载bean标签来创建对象。
2.调用API获取IOC创建的对象。
两种方式
2.1 通过id获取对象
//1.加载spring.xml配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");//2.通过id值获取对象
Employee emp = (Employee) applicationContext.getBean("emp");
System.out.println(emp);
2.2 通过运行时类获取对象
注意: 当spring.xml中配置两个Employee的bean时程序报错,因为此时两个bean都是由Employee类生成的,IOC容器无法将两个bean都返回。
必须指定一个唯一的bean
spring.xml
TestSpringEmp.java
//1.加载spring.xml配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");//2.通过运行时类获取对象
Employee emp = applicationContext.getBean(Employee.class);
System.out.println(emp);
无参构造代码
spring.xml
TestSpringEmp.java
importcom.spring.model.Employee;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;public classTestSpringEmp {public static voidmain(String[] args) {//1.加载spring.xml配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");//2.通过id值获取对象
Employee emp = (Employee) applicationContext.getBean("emp");
System.out.println(emp);
}
}
第一步:加载spring.xml配置文件,生成ApplicationContext对象。
第二步:调用ApplicationContext的getBean方法获取对象,参数为配置文件中的id值。
程序在加载spring.xml时创建stu对象,通过反射机制调用无参构造函数,所有要求交给IOC容器管理的类必须有无参构造函数。
如何赋值呢?
调用无参构造只会创建对象而不会进行赋值,如何赋值呢?只需要在spring.xml中进行相关配置即可。
添加property标签:name对应属性名,value是属性的值。
注:若包含特殊字符,比如name="",使用]]>进行配置。
spring.xml
]]>
有参构造代码
在实体类中创建有参构造
Employee.java
public Employee(intid, String employeeNo, String employeeName) {super();this.id =id;this.employeeNo =employeeNo;this.employeeName =employeeName;
}
spring.xml
除了使用name对应参数外,还可以通过下标index对应。
进阶1:多个对象级联关系?
创建工作经历类 job.java
public classJob {/**序号*/
private intid;/**单位名称*/
privateString companyName;/**职位名称*/
privateString position;/**工作薪水*/
privateBigDecimal salary;/**setter and getter*/}
在员工Employee 类中添加工作经历job 类:
Employee.java
/**工作经历类*/
privateJob job;/**系统id*/
private intid;/**员工编号*/
privateString employeeNo;/**员工姓名*/
private String employeeName;
spring.xml中配置Job 对象,然后将该对象赋值给emp 对象。
spring.xml
]]>
在spring.xml中,通过ref属性将其他bean赋给当前bean对象,这种方式叫做依赖注入(DI),是Spring非常重要的机制,DI是将不同对象进行关联的一种方式,是IOC的具体实现方式,通常DI和IOC是紧密结合在一起的,所以一般说的IOC包括DI。
如果是集合属性如何依赖注入?
Job 类中添加List属性。
Job.java
/**List属性*/
private List employeeList;
spring.xml中配置2个emp对象,1个job对象,并将2个emp对象注入到job对象中。
spring.xml
]]>
]]>
集合属性通过list标签和ref标签完成注入。ref的bean属性指向需要注入的bean对象。
IoC和DI
2004年大师级人物Martin Fowler:“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
DI—Dependency Injection,即“依赖注入”:我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”。
谁依赖于谁:当然是应用程序依赖于IoC容器;
为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI 是什么关系呢?
其实它们是同一个概念的不同角度描述。
IoC 是spring的核心,所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。由spring来负责控制对象的生命周期和对象间的关系,这叫控制反转。
Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。
控制的什么被反转了?就是:获得依赖对象的方式反转了。
案例2:工厂方法
IOC是典型的工厂模式,IOC通过工厂模式创建bean有两种方式:
1.静态工厂方法
2.实例工厂方法
一.静态工厂方法
1.1 创建工作实体类
job.java
public classJob {/**序号*/
private intid;/**单位名称*/
privateString companyName;/**职位名称*/
privateString position;public Job(intid, String companyName, String position) {super();this.id =id;this.companyName =companyName;this.position =position;
}publicJob() {super();
}
@OverridepublicString toString() {return "Job [id=" + id + ", companyName=" + companyName + ", postion=" + position + " ]";
}
}
1.2 创建静态工厂类,静态工厂方法。
StaticFactoryJob.java
private static Mapjobs;static{
jobs= new HashMap<>();
jobs.put(1, new Job(1, "猫厂", "p7"));
jobs.put(2, new Job(2, "鹅厂","T2"));
}public static Job getJob(intid) {returnjobs.get(id);
}
1.3 在spring.xml中配置静态工厂。
spring.xml
factory-method指向静态方法。
constructor-arg 的value属性为调用静态方法所传的参数。
1.4 在测试类中直接获取job1对象。 TestSpringStatic.java
public static void main(String[] args) throwsSQLException {
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
Job job= (Job) applicationContext.getBean("job1");
System.out.println(job);
}//打印:Job [id=1, companyName=猫厂, postion=p7 ]
二.实例工厂方法
2.1 创建实例工厂类,工厂方法 。
InstanceFactoryJob.java
private Mapjobs;publicInstanceFactoryJob() {
jobs= new HashMap<>();
jobs.put(1, new Job(1, "猫厂", "p7"));
jobs.put(2, new Job(2, "鹅厂", "T2"));
}public Job getJob(intid) {returnjobs.get(id);
}
2.2 spring.xml 中配置 bean
spring.xml
2.3 在测试类中直接获取car2对象
public static void main(String[] args) throwsSQLException {
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
Job job= (Job) applicationContext.getBean("job2");
System.out.println(job);
}//打印: Job [id=2, companyName=鹅厂, postion=T2 ]
区别
静态工厂方法的方式创建job对象,不需要实例化工厂对象,因为静态工厂的静态方法,不需要创建对象即可调用。所以spring.xml只需要配置一个Job bean,而不需要配置工厂bean。
实例工厂方法创建job对象,必须先实例化工厂对象,因为调用的是非静态方法,必须通过对象调用,不能直接通过类来调用,所以spring.xml中需要先配置工厂bean,再配置Job bean。
案例3:IOC自动装载(autowire)
自动装载有两种方式:
byName:通过属性名自动装载
byType:通过属性对应的数据类型自动装载
3.1 通过属性名自动装载
1 新建BaseEmployee.java 类
public classBaseEmployee {/**系统id*/
private intid;/**员工编号*/
privateString employeeNo;/**员工姓名*/
privateString employeeName;/**工作经历*/
privateJob job;/**setter and getter*/@OverridepublicString toString() {return "BaseEmployee [id=" + id + ", employeeNo=" + employeeNo + ", employeeName=" + employeeName + ", job=" + job + "]";
}
}
2 spring.xml中配置Car bean和Person bean,并通过自动装载进行依赖注入。
spring.xml
注意:通过property标签手动进行car的注入优先级更高,若两种方式同时配置,以property的配置为准。
3 测试类中获取baseEmployee 对象。
TestSpringBaseEmployee.java
public classTestSpringBaseEmployee {public static voidmain(String[] args) {
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
BaseEmployee emp= (BaseEmployee) applicationContext.getBean("emp");
System.out.println(emp);
}
}
3.2 通过属性对应的数据类型自动装载
知识点:使用byType进行自动装载时,spring.xml中只能配置一个装载的bean。
1.spring.xml
2.测试类中获取person对象
TestSpringBaseEmployee.java
//打印:BaseEmployee [id=1, employeeNo=10001, employeeName=一只阿木木, job=Job [id=2, companyName=鹅厂, postion=T2 ]]
案例4:程序架构MVC 分层
经典三层架构:Controller层,Service层,DAO层。
有两种方式:
基于xml配置文件
基于注解
4.1 基于 xml 配置
model 层实体类:
BaseEmployee.java
public classBaseEmployee {/**系统id*/
private intid;/**员工编号*/
privateString employeeNo;/**员工姓名*/
privateString employeeName;public BaseEmployee(intid, String employeeNo, String employeeName) {super();this.id =id;this.employeeNo =employeeNo;this.employeeName =employeeName;
}publicBaseEmployee() {super();
}
@OverridepublicString toString() {return "BaseEmployee [id=" + id + ", employeeNo=" + employeeNo + ", employeeName=" + employeeName + "]";
}/**setter and getter*/}
Dao 层接口
BaseEmployeeDao.java
public interfaceBaseEmployeeDao {public BaseEmployee getBaseEmpById(intid);
}
BaseEmployeeDaoImpl.java
public class BaseEmployeeDaoImpl implementsBaseEmployeeDao{private static MapbaseEmployee;static{
baseEmployee= new HashMap();
baseEmployee.put(1, new BaseEmployee(1, "10001", "一只阿木木"));
baseEmployee.put(2, new BaseEmployee(2, "10002", "两只阿木木"));
baseEmployee.put(3, new BaseEmployee(3, "10003", "三只阿木木"));
}
@Overridepublic BaseEmployee getBaseEmpById(intid) {//TODO Auto-generated method stub
returnbaseEmployee.get(id);
}
}
Service 层
创建BaseEmployeeService 接口以及实现类BaseEmployeeServiceImpl。
BaseEmployeeService
public interfaceBaseEmployeeService {public BaseEmployee getBaseEmpById(intid);
}
BaseEmployeeServiceImpl.java
public class BaseEmployeeServiceImpl implementsBaseEmployeeService {privateBaseEmployeeDAO baseEmployeeDAO;publicBaseEmployeeDAO getBaseEmployeeDAO() {returnbaseEmployeeDAO;
}public voidsetBaseEmployeeDAO(BaseEmployeeDAO baseEmployeeDAO) {this.baseEmployeeDAO =baseEmployeeDAO;
}
@Overridepublic User getBaseEmpById(intid) {//TODO Auto-generated method stub
returnbaseEmployeeDAO.getBaseEmpById(id);
}
}
Controller 层
BaseEmployeeController.java
public classBaseEmployeeController {privateBaseEmployeeService baseEmployeeService;publicBaseEmployeeService getBaseEmployeeService() {returnbaseEmployeeService;
}public voidsetBaseEmployeeService(BaseEmployeeService baseEmployeeService) {this.baseEmployeeService =baseEmployeeService;
}public BaseEmployee getBaseEmpById(intid){returnbaseEmployeeService.getBaseEmpById(id);
}
}
spring.xml
在spring.xml配置Controller,Service,DAO,并完成依赖注入。 spring.xml
测试
TestSpringBaseEmployee.java
public static voidmain(String[] args) {
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("spring.xml");
BaseEmployeeController baseEmployeeController= (BaseEmployeeController) applicationContext.getBean("baseEmployeeController");
BaseEmployee baseEmployee= baseEmployeeController.getBaseEmpById(1);
System.out.println(baseEmployee);
}
4.2基于注解的方式
4.2.1 默认byType 方式
第一步:将Controller,Service,DAO类扫描到IOC容器中。
第二步:在类中设置注解完成依赖注入。
1 修改 spring.xml
知识点:引入context 命名空间
2 修改DAOImpl
改动:在类名处添加@Repository注解,表示该类是数据接口层。
@Repositorypublic class BaseEmployeeDaoImpl implementsBaseEmployeeDao{private static MapbaseEmployee;static{
baseEmployee= new HashMap();
baseEmployee.put(1, new BaseEmployee(1, "10001", "一只阿木木"));
baseEmployee.put(2, new BaseEmployee(2, "10002", "两只阿木木"));
baseEmployee.put(3, new BaseEmployee(3, "10003", "三只阿木木"));
}
@Overridepublic BaseEmployee getBaseEmpById(intid) {//TODO Auto-generated method stub
returnbaseEmployee.get(id);
}
}
3 修改ServiceImpl。
在类名处添加@Service注解,表示该类是业务层。
DAO属性出添加@Autowired注解,表示IOC容器自动完成装载,默认是byType的方式。
@Servicepublic class BaseEmployeeServiceImpl implementsBaseEmployeeService{
@AutowiredprivateBaseEmployeeDAO baseEmployeeDAO;
@Overridepublic BaseEmployee getBaseEmpById(intid) {//TODO Auto-generated method stub
returnbaseEmployeeDAO.getBaseEmpById(id);
}
}
4 修改 Controller类,添加注解。
有两处改动:
在类名处添加@Controller注解,表示该类作为一个控制器。
Service属性出添加@Autowired注解,表示IOC容器自动完成装载,默认是byType的方式。
BaseEmployeeControler.java
@Controllerpublic classBaseEmployeeController {
@AutowiredprivateBaseEmployeeService baseEmployeeService;public BaseEmployee getBaseEmpById(intid){returnbaseEmployeeService.getBaseEmpById(id);
}
}
4.2.2 ByName
自动装载除了byType的方式,还可以结合@Qualifier注解, 使用byName的方式。
知识点:@Qualifier()中的值必须与@Service()中的值一致,才能完成自动装载。
BaseEmployeeControler.java
@Controllerpublic classBaseEmployeeController {
@Autowired
@Qualifier("baseEmployeeService")privateBaseEmployeeService baseEmployeeService;public BaseEmployee getBaseEmpById(intid){returnbaseEmployeeService.getBaseEmpById(id);
}
}
基于注解的方式我们并没有给bean设置id,byName的方式是通过属性名去匹配对应bean的id属性值。
添加注解时,类名首字母小写之后的值就是id的默认值。IOC容器中默认赋值,BaseEmployeeService bean的id=baseEmployeeService,与Controller中的属性名一致
@Servicepublic class BaseEmployeeServiceImpl implements BaseEmployeeService
修改
BaseEmployeeService bean的id=sortBaseEmployeeService。
Service 层
@Service("sortBaseEmployeeService")public class BaseEmployeeServiceImpl implementsBaseEmployeeService{
@AutowiredprivateBaseEmployeeDAO baseEmployeeDAO;
@Overridepublic BaseEmployee getBaseEmpById(intid) {//TODO Auto-generated method stub
returnbaseEmployeeDAO.getBaseEmpById(id);
}
}
Controller 层
Controller中的Service属性也需要去匹配name=sortBaseEmployeeService的bean,所以设置@Qualifier("sortBaseEmployeeService")。
@Controllerpublic classBaseEmployeeController {
@Autowired
@Qualifier("sortBaseEmployeeService")privateBaseEmployeeService baseEmployeeService;public BaseEmployee getBaseEmpById(intid){returnbaseEmployeeService.getBaseEmpById(id);
}
}
小结
IOC概念
spring IoC的思想:依赖注入就是A开放接口,将B传递进来(注入);控制反转,AB双方不相互依赖,整个活动的进行由第三方负责管理。
控制反转,由容器创建组件对象,然后注入参数建立应用关系。
例如EmployeeDao调用Data,由Spring容器创建EmployeeDao和Data对象,然后再由容器将Data对象注入给EmployeeDao中的属性。
Data注入途径可以是set方法,也可以是带参数构造器等。
编写规则
EmployeeDao类在编写时,需要定义一个set方法或带参数构造器,参数类型为Data。
public classEmployeeDao {privateData data;//1 set 方法注入
private voidsetData(Data data) {this.data =data;
}//2 带参数构造器注入
privateEmployeeDao(Data data) {this.data =data;
}
}
配置原理
装配 Spring Bean 小结
集合注入总结:
List 属性使用 元素定义注入,使用多个 元素的 Bean 属性去引用之前定义好的 Bean
Map 属性使用 元素定义注入,使用多个 元素的 key-ref 属性去引用之前定义好的 Bean 作为键,而用 value-ref 属性引用之前定义好的 Bean 作为值
Set 属性使用 元素定义注入,使用多个 元素的 bean 去引用之前定义好的 Bean
命名空间装配
需要引入其声明
C命名空间
p-命名空间
c-命名空间通过构造器注入的方式来配置 bean,p-命名空间则是用setter的注入方式来配置 bean
util-命名空间
工具类的命名空间,可以简化集合类元素的配置
引入配置文件
【src】文件下新建一个 bean.xml
在 applicationContext.xml 文件中写入:
自动装配 autowired
理解: @Autowired 注解表示在 Spring IoC 定位所有的 Bean 后,再根据类型寻找资源,然后将其注入。
过程: 定义 Bean ——> 初始化 Bean(扫描) ——> 根据属性需要从 Spring IoC 容器中搜寻满足要求的 Bean ——> 满足要求则注入
问题: IoC 容器可能会寻找失败,此时会抛出异常(默认情况下的Spring IoC 容器会认为一定要找到对应的 Bean 来注入到这个字段,但有些时候并不是一定需要,比如日志)
解决: 通过配置项 required 来改变,比如 @Autowired(required = false)
一切需要 Spring IoC 去寻找 Bean 资源的地方都可以用到
自动装配的歧义性(@Primary和@Qualifier)
@Primary 注解:
首要的。当 Spring IoC 检测到有多个相同类型的 Bean 资源的时候,会优先注入使用该注解的类。
问题:只是解决了首要的问题,但是并没有选择性的问题
@Qualifier 注解:
产生歧义性的一个重要的原因是 Spring 在寻找依赖注入的时候是按照类型注入引起的。除了按类型查找 Bean,Spring IoC 容器最底层的接口 BeanFactory 还提供了按名字查找的方法,如果按照名字来查找和注入不就能消除歧义性了吗?
使用方法: 指定注入名称为 "source1" 的 Bean 资源
/*包名和import*/
public classSpringZhujie {
......
@Autowired
@Qualifier("source1")public voidsetSource(Source source) {this.source =source;
}
}
使用@Bean 装配 Bean
问题:
通过 @Component 注解来装配 Bean ,并且只能注解在类上,当你需要引用第三方包的(jar 文件),而且往往并没有这些包的源码,这时候将无法为这些包的类加入 @Component 注解,让它们变成开发环境中的 Bean 资源。
解决方案:
1.自己创建一个新的类来扩展包里的类,然后再新类上使用 @Component 注解,但这样很 low
2.使用 @Bean 注解,注解到方法之上,使其成为 Spring 中返回对象为 Spring 的 Bean 资源。
@Configuration 注解相当于 XML 文件的根元素,解析其中的 @Bean 注解
importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;
@Configurationpublic classTestBean {
@Bean(name= "testBean")publicString test() {
String str= "测试@Bean注解";returnstr;
}
}
使用方法
引入spring 开发包
配置文件.xml
编写组件,定义到xml 配置中
创建窗口对象,调用 getBean 获取Bean 对象使用
由于Java语言本身的类反射功能,使得仅凭一个配置文件,就能魔法般地实例化并装配好程序所用的Bean。容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的装配,从容器中即可返回准备就绪的Bean实例,后续可直接使用之。
公众号:一只阿木木