0 Spring课程
0.1 Spring入门篇 目录
https://www.cnblogs.com/1446358788-qq/articles/13070140.html
1 Spring核心
1.1 Spring IOC(控制反转)原理
归纳总结:
控制反转的原理之一:依赖倒置原则
依赖倒置原则:高层模块不能依赖底层模块,他们依赖他们共同的接口;
控制反转容器:用configuration替代了多个new 对象的步骤,提高了编程效率;
控制反转优点:
1 增加了代码的可维护性,节约了修改成本;
2 省略了非必要new对象的步骤,提高了开发效率;
3 提高不同分组的协同合作,成倍提高开发效率;

转自:https://www.zhihu.com/question/23277575/answer/169698662
要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Principle )。
什么是依赖倒置原则?假设我们设计一辆汽车:先设计轮子,然后根据轮子大小设计底盘,接着根据底盘设计车身,最后根据车身设计好整个汽车。这里就出现了一个“依赖”关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子。

这样的设计看起来没问题,但是可维护性却很低。假设设计完工之后,上司却突然说根据市场需求的变动,要我们把车子的轮子设计都改大一码。这下我们就蛋疼了:因为我们是根据轮子的尺寸设计的底盘,轮子的尺寸一改,底盘的设计就得修改;同样因为我们是根据底盘设计的车身,那么车身也得改,同理汽车设计也得改——整个设计几乎都得改!
我们现在换一种思路。我们先设计汽车的大概样子,然后根据汽车的样子来设计车身,根据车身来设计底盘,最后根据底盘来设计轮子。这时候,依赖关系就倒置过来了:轮子依赖底盘, 底盘依赖车身, 车身依赖汽车。

这时候,上司再说要改动轮子的设计,我们就只需要改动轮子的设计,而不需要动底盘,车身,汽车的设计了。
这就是依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况。

控制反转(Inversion of Control) 就是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection)。其实这些概念初次接触都会感到云里雾里的。说穿了,这几种概念的关系大概如下:

为了理解这几个概念,我们还是用上面汽车的例子。只不过这次换成代码。我们先定义四个Class,车,车身,底盘,轮胎。然后初始化这辆车,最后跑这辆车。代码结构如下:


这样,就相当于上面第一个例子,上层建筑依赖下层建筑——每一个类的构造函数都直接调用了底层代码的构造函数。假设我们需要改动一下轮胎(Tire)类,把它的尺寸变成动态的,而不是一直都是30。我们需要这样改:

由于我们修改了轮胎的定义,为了让整个程序正常运行,我们需要做以下改动:


由此我们可以看到,仅仅是为了修改轮胎的构造函数,这种设计却需要修改整个上层所有类的构造函数!在软件工程中,这样的设计几乎是不可维护的——在实际工程项目中,有的类可能会是几千个类的底层,如果每次修改这个类,我们都要修改所有以它作为依赖的类,那软件的维护成本就太高了。
所以我们需要进行控制反转(IoC),及上层控制下层,而不是下层控制着上层。我们用依赖注入(Dependency Injection)这种方式来实现控制反转。所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。这里我们用构造方法传递的依赖注入方式重新写车类的定义:

这里我们再把轮胎尺寸变成动态的,同样为了让整个系统顺利运行,我们需要做如下修改:

看到没?这里我只需要修改轮胎类就行了,不用修改其他任何上层类。这显然是更容易维护的代码。不仅如此,在实际的工程中,这种设计模式还有利于不同组的协同合作和单元测试:比如开发这四个类的分别是四个不同的组,那么只要定义好了接口,四个不同的组可以同时进行开发而不相互受限制;而对于单元测试,如果我们要写Car类的单元测试,就只需要Mock一下Framework类传入Car就行了,而不用把Framework, Bottom, Tire全部new一遍再来构造Car。
这里我们是采用的构造函数传入的方式进行的依赖注入。其实还有另外两种方法:Setter传递和接口传递。这里就不多讲了,核心思路都是一样的,都是为了实现控制反转。


看到这里你应该能理解什么控制反转和依赖注入了。那什么是控制反转容器(IoC Container)呢?其实上面的例子中,对车类进行初始化的那段代码发生的地方,就是控制反转容器。

显然你也应该观察到了,因为采用了依赖注入,在初始化的过程中就不可避免的会写大量的new。这里IoC容器就解决了这个问题。这个容器可以自动对你的代码进行初始化,你只需要维护一个Configuration(可以是xml可以是一段代码),而不用每次初始化一辆车都要亲手去写那一大段初始化的代码。这是引入IoC Container的第一个好处。
IoC Container的第二个好处是:我们在创建实例的时候不需要了解其中的细节。在上面的例子中,我们自己手动创建一个车instance时候,是从底层往上层new的:

这个过程中,我们需要了解整个Car/Framework/Bottom/Tire类构造函数是怎么定义的,才能一步一步new/注入。
而IoC Container在进行这个工作的时候是反过来的,它先从最上层开始往下找依赖关系,到达最底层之后再往上一步一步new(有点像深度优先遍历):

这里IoC Container可以直接隐藏具体的创建实例的细节,在我们来看它就像一个工厂:

我们就像是工厂的客户。我们只需要向工厂请求一个Car实例,然后它就给我们按照Config创建了一个Car实例。我们完全不用管这个Car实例是怎么一步一步被创建出来。
实际项目中,有的Service Class可能是十年前写的,有几百个类作为它的底层。假设我们新写的一个API需要实例化这个Service,我们总不可能回头去搞清楚这几百个类的构造函数吧?IoC Container的这个特性就很完美的解决了这类问题——因为这个架构要求你在写class的时候需要写相应的Config文件,所以你要初始化很久以前的Service类的时候,前人都已经写好了Config文件,你直接在需要用的地方注入这个Service就可以了。这大大增加了项目的可维护性且降低了开发难度。

这里只是很粗略的讲了一下我自己对IoC和DI的理解。主要的目的是在于最大限度避免晦涩难懂的专业词汇,用尽量简洁,通俗,直观的例子来解释这些概念。如果让大家能有一个类似“哦!原来就是这么个玩意嘛!”的印象,我觉得就OK了。想要深入了解的话,可以上网查阅一些更权威的资料。这里推荐一下 Dependency injection 和 Inversion of Control Containers and the Dependency Injection pattern 这两篇文章,讲的很好很详细。
2 Spring 代码演练
2.1 代码演练 依赖注入两种注入方式
1 依赖注入的两种注入方式
2 依赖注入的两种注入方式代码实现
3 依赖注入两种注入方式的区别及优缺点
4 如何解决这个问题
5 引用链接
1 依赖注入的两种注入方式
依赖注入可以分为set注入和构造函数注入
set注入需要注意:注入bean的时候,bean的名字 要和 实体类起的类名相同。
2 依赖注入的两种注入方式代码实现
1 依赖注入的实现方式:
实现类:
package com.springnovel.payment.springmixed;
import java.math.BigDecimal;
import org.springframework.beans.factory.annotation.Required;
import com.springnovel.dao.IOrderDao;
import com.springnovel.perfectlogger.ILogger;
/**
* @author weijingli
*
*/
public class PaymentAction_SetInjection {
private ILogger logger;
public PaymentAction_SetInjection(ILogger logger ) {
super();
this.logger = logger;
}
public void pay(BigDecimal payValue){
logger.log("开始支付,支付金额为:"+payValue);
logger.log("支付结束;");
}
}
payment.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">
<!-- 使用了<constructor-arg>标签,指定了ServerLogger作为PaymentAction构造函数的入参: -->
<bean id="paymentAction_setInjection" class="com.springnovel.payment.springmixed.PaymentAction_SetInjection">
<constructor-arg ref="serverLogger"></constructor-arg>
</bean>
<bean id="serverLogger" class="com.springnovel.perfectlogger.ServerLogger"/>
</beans>
ServerLogger类:
package com.springnovel.perfectlogger;
public class ServerLogger implements ILogger{
@Override
public void log(String logInfo) {
System.out.println("服务器打印开始:"+logInfo);
}
}
2 set注入的实现方式:
实现类
package com.springnovel.payment.springmixed;
import java.math.BigDecimal;
import org.springframework.beans.factory.annotation.Required;
import com.springnovel.dao.IOrderDao;
import com.springnovel.perfectlogger.ILogger;
public class PaymentAction_SetInjection {
private IOrderDao orderDao;
public IOrderDao getOrderDao() {
return orderDao;
}
public void setOrderDao(IOrderDao orderDao) {
this.orderDao = orderDao;
}
public void updateOrderAfterPayment(String orderId){
orderDao.updateOrderAfterPayment(orderId);
}
}
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">
<!-- 使用了<property>标签,set入参: -->
<bean id="paymentAction_setInjection" class="com.springnovel.payment.springmixed.PaymentAction_SetInjection">
<property name="orderDao" ref="orderDao"/>
</bean>
<bean id="orderDao" class="com.springnovel.dao.OrderDao"/>
</beans>
dao类
package com.springnovel.dao;
public class OrderDao implements IOrderDao {
@Override
public void deleteOrder(String orderId) {
// TODO Auto-generated method stub
System.out.println("删除订单成功,本次删除订单号为:"+orderId);
}
@Override
public void updateOrderAfterPayment(String orderId) {
// TODO Auto-generated method stub
System.out.println("订单支付成功,以修改订单"+orderId+"状态为已支付!");
}
}
3 依赖注入两种注入方式的区别及优缺点
区别:
- Constructor注入能够强制要求调用者注入构造函数中的所有参数,否则在容器初始化时就会失败;但是如果要注入的对象过多,就会导致构造函数过于庞大。
- Setter注入,类似于Builder模式,将原本庞大的构造函数,拆解为了一个小的构造函数和许多个set方法。setter注入不能保证对象一定会被注入,但是可以使用@Required注解,强制要求使用者注入对象,否则在容器初始化时就会报错。
4 如何解决这个问题
解决方法:set注入方式添加required注解。
开启Spring注解的功能:
payment.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"
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/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- 使用了<constructor-arg>标签,指定了ServerLogger作为PaymentAction构造函数的入参: -->
<!-- <bean id="paymentActionMixed" class="com.springnovel.payment.springmixed.PaymentActionMixed">
<constructor-arg ref="serverLogger"></constructor-arg>
<constructor-arg ref="orderDao"></constructor-arg>
</bean> -->
<!-- 使用了<property>标签,set入参: -->
<bean id="paymentAction_setInjection" class="com.springnovel.payment.springmixed.PaymentAction_SetInjection">
<!-- <property name="serverLogger" ref="serverLogger"/> -->
<!-- <property name="orderDao" ref="orderDao"/> -->
</bean>
<bean id="serverLogger" class="com.springnovel.perfectlogger.ServerLogger"/>
<bean id="orderDao" class="com.springnovel.dao.OrderDao"/>
<context:annotation-config/>
<!-- <context:component-scan base-package="com.springnovel.perfectlogger"></context:component-scan> -->
</beans>
实体类:PaymentAction_SetInjection.java(红色标注部分)
private ILogger serverLogger;
private IOrderDao orderDao;
@Required
public void setServerLogger(ILogger serverLogger) {
this.serverLogger = serverLogger;
}
public void setOrderDao(IOrderDao orderDao) {
this.orderDao = orderDao;
}
这样一启动服务器就会直接报错,避免了生产运行过程中的隐形bug
5 引用链接
本文参考文章连接:
https://zhuanlan.zhihu.com/p/29426019
项目本地路径:
F:\xiangmu3\Xin\FuQiang\Spring
项目百度云路径:
链接:https://pan.baidu.com/s/1VrIHyaGfogAmPDZ8Vsw7vg
提取码:ewgv
2.2 代码演练 为何需要依赖注入
0 总结
1 代码实现之为何需要依赖注入
2 代码实现之工厂模式和依赖注入 实现代码及优缺点
3 代码实现之如何进行单元测试
4 引用链接
0 总结
a 为何使用依赖注入
I 使用依赖注入对于需求变更不在需要大量的修改代码,简化了工程量
II 使用依赖注入可以用高内聚低耦合的方式实现代码
III 使用依赖注入可以在数据库发生故障或者项目进度需要时,进行单元测试,节省人力成本
IIII 与工厂模式创建对象相比,他不用每次都创建对象,节省了大量的内存
1 为何需要依赖注入
需求a: 电商项目需要在支付订单接口实现打印日志到文件上的功能,生产有问题方便排查
解决方案:
支付订单类:(详细代码下载见底部)https://pan.baidu.com/s/1m0vDEKOLE5YCry93lubYMA
package com.springnovel.payment.withoutspring;
import java.math.BigDecimal;
import com.springnovel.perfectlogger.Filelogger;
import com.springnovel.perfectlogger.ILogger;
/**
* 实现功能:日志支付接口打印日志
* @author weijingli
* 特点:ILogger 有多个实现类
* FileLogger
* ConsoleLogger
* ServerLogger
*
* 本类缺点:如果将日志打印到本地变更为打印到日志服务器,所有涉及日志的创建对象部分需要进行全量替换,这种情况下代码不合理
*/
public class PaymentActionV1 {
private ILogger logger = new Filelogger();
//此处使用BigDecimal,支付接口必须使用最精确的结果
public void pay(BigDecimal payValue){
logger.log("开始打印支付日志,支付金额为"+payValue);
//do pay ...
logger.log("打印支付日志结束");
}
}
打印到文件日志 实现类:
package com.springnovel.perfectlogger;
public class Filelogger implements ILogger{
@Override
public void log(String logInfo) {
System.out.println("FileLogger:"+logInfo);
}
}
需求 b:在a的基础上,我所有的接口都需要实现打印日志功能
解决方案:(代码类似上部代码)
一个月后:
需求 c:日志文件满了,磁盘不够,公司买了新的服务器,以后打印日志到服务器上
解决方案:由于日志接口有文件日志实现类和服务器日志实现类。如果全量变更new对象,需求改动量太大。因此可以使用工厂模式或者依赖注入的方式解决这个问题
2 工厂模式和依赖注入 实现代码及优缺点
工厂模式解决上述问题:
支付订单类
package com.springnovel.payment.withoutspring;
import java.math.BigDecimal;
import com.springnovel.perfectlogger.ILogger;
/**
* 实现功能:日志打印的功能
* @author weijingli
* 功能优点:可自由切换打印日志到服务器或者本地路径,只需要修改工厂类:loggerFactory即可
* 功能缺点: 1 工厂类每次都new一个新对象,是不是很浪费,能不能做成单例的,甚至是做成单例和多例是可以配置;
* 2 如果有这种需求:支付信息比较多而且比较敏感,日志要打印到远程服务器,其他信息都打印到本地,怎么实现;
*/
public class PaymentActionV2 {
private ILogger logger = LoggerFactory.createLogger();
public void pay(BigDecimal payValue){
logger.log("支付开始:"+payValue);
//此处省略一亿字
logger.log("支付结束:");
}
}
工厂类:
package com.springnovel.payment.withoutspring;
import com.springnovel.perfectlogger.Filelogger;
import com.springnovel.perfectlogger.ILogger;
import com.springnovel.perfectlogger.ServerLogger;
public class LoggerFactory {
private static ILogger logger = new ServerLogger();
//工厂类的方法静态为好,被调用
public static ILogger createLogger(){
return logger;
}
}
依赖注入方式解决上述问题:
支付类依赖日志类,通过payment.xml将日志类注入到支付类即可
支付订单类:
package com.springnovel.payment.springmixed;
import java.math.BigDecimal;
import com.springnovel.perfectlogger.ILogger;
//使用spring的payment.xml方式将log注入到本类中
public class PaymentActionMixed {
private ILogger logger;
/**
* 此处要记,如果注入,构造方法参数必须把注入的类加进去
* @param logger
*/
//通过构造方法将logger传入本类
public PaymentActionMixed(ILogger logger) {
super();
this.logger=logger;
// TODO Auto-generated constructor stub
}
public void pay(BigDecimal payValue){
logger.log("开始支付,支付金额为:"+payValue);
logger.log("支付结束;");
}
}
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">
<!-- 使用了<constructor-arg>标签,指定了ServerLogger作为PaymentAction构造函数的入参: -->
<bean id="paymentActionMixed" class="com.springnovel.payment.springmixed.PaymentActionMixed">
<constructor-arg ref="serverLogger"/>
</bean>
<bean id="serverLogger" class="com.springnovel.perfectlogger.ServerLogger"/>
</beans>
测试demo:
package com.springnovel.test;
import java.math.BigDecimal;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.springnovel.payment.springmixed.PaymentActionMixed;
import com.springnovel.payment.withoutspring.PaymentActionV1;
import com.springnovel.payment.withoutspring.PaymentActionV2;
public class PaymentTest {
@Test
public void testPaymentActionV1(){
PaymentActionV1 pav1 = new PaymentActionV1();
pav1.pay(new BigDecimal(1));//此处传参应为bigdecimal类型
}
@Test
public void testPaymentActionV2(){
PaymentActionV2 pav2 = new PaymentActionV2();
pav2.pay(new BigDecimal(2));
}
/**
* 此处需要记录
*/
@Test
//此处要加载payment.xml文件 和 得到bean对象呀
public void testPaymentActionMixed(){
ApplicationContext context = new ClassPathXmlApplicationContext("payment.xml");
PaymentActionMixed pam = (PaymentActionMixed) context.getBean("paymentActionMixed");
pam.pay(new BigDecimal(7));
}
}
工厂类缺点:浪费内存
3 如何进行单元测试
问题a:数据库挂了,我要测试业务逻辑删除订单部分,代码如何实现?
测试类:
package com.springnovel.test;
import java.math.BigDecimal;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.springnovel.dao.IOrderDao;
import com.springnovel.order.springxml.OrderAction;
import com.springnovel.payment.springmixed.PaymentActionMixed;
import com.springnovel.payment.withoutspring.PaymentActionV1;
import com.springnovel.payment.withoutspring.PaymentActionV2;
public class OrderTest {
@Test
public void testDeleteOrder(){
ApplicationContext ac = new ClassPathXmlApplicationContext("order.xml");
OrderAction oa = (OrderAction)ac.getBean("orderAction");
oa.deleteOrder("12345");
}
}
order.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">
<!-- 使用了<constructor-arg>标签,指定了ServerLogger作为PaymentAction构造函数的入参: -->
<bean id="orderAction" class="com.springnovel.order.springxml.OrderAction">
<constructor-arg ref="orderDao"/>
</bean>
<bean id="orderDao" class="com.springnovel.dao.OrderDao"/>
</beans>
实现类:
package com.springnovel.order.springxml;
import com.springnovel.dao.IOrderDao;
/*
* 单元测试案例:
* 如果要测试逻辑部分,可以使用此处直接测试
*
* 功能:订单删除,要求采用spring方式,不连接数据库,就能删除数据
*/
public class OrderAction {
private IOrderDao orderDao;
public OrderAction(IOrderDao orderDao) {
super();
// TODO Auto-generated constructor stub
this.orderDao = orderDao;
}
public void deleteOrder(String orderId){
orderDao.deleteOrder(orderId);
}
}
4 引用链接
本文参考文章连接:
https://zhuanlan.zhihu.com/p/29426019
项目本地路径:
F:\xiangmu3\Xin\FuQiang\Spring
项目百度云路径:
https://pan.baidu.com/s/1m0vDEKOLE5YCry93lubYMA
提取码:kx0s
3 Spring 报错
3.1 Spring错误:Exception encountered during context initialization
1 完整报错:
2 错误分析:
3 解决方法:
1 完整报错:
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'injectionServiceImpl' defined in file [F:\xiangmu3\Xin\FuQiang\Spring\ddwei-dao\target\classes\com\imooc\beanannotation\injection\service\InjectionServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.imooc.bean.ioc.interfaces.InjectionDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'injectionServiceImpl' defined in file [F:\xiangmu3\Xin\FuQiang\Spring\ddwei-dao\target\classes\com\imooc\beanannotation\injection\service\InjectionServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.imooc.bean.ioc.interfaces.InjectionDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095)
2 错误分析:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
通过分析报错部分:依赖注入的注解,期望至少有一个类是自动装配达标。
说明是没有添加注解呗,通过百度搜索,很多报这种类型的错误的都是bean没有加注解。
分析我本地的报错:
service 引用的dao类有两个package,一个有注解,一个没有,我的package引用的是没有注解的那个。
3 解决方法:
更新package即可
3.2 Spring 错误 Bean property 'serverLogger' is not writable or has an invalid setter method.
使用spring set注入方式,报错如上:类的属性有一个非法的set 方法。
经查询:
payment.xml如下:
<!-- 使用了<property>标签,set入参: -->
<bean id="paymentAction_setInjection" class="com.springnovel.payment.springmixed.PaymentAction_SetInjection">
<property name="serverLogger" ref="serverLogger"/>
<property name="orderDao" ref="orderDao"/>
</bean>
<bean id="serverLogger" class="com.springnovel.perfectlogger.ServerLogger"/>
<bean id="orderDao" class="com.springnovel.dao.OrderDao"/>
PaymentAction_SetInjection如下:
private ILogger logger;
private IOrderDao orderDao;
public void setLogger(ILogger logger) {
this.logger = logger;
}
public void setOrderDao(IOrderDao orderDao) {
this.orderDao = orderDao;
}
知:set方法的name值必须 和 实体类的声明的变量名一致。
PaymentAction_SetInjection修改方法如下(红色部分):
private ILogger serverLogger;
private IOrderDao orderDao;
public void setServerLogger(ILogger serverLogger) {
this.serverLogger = serverLogger;
}
public void setOrderDao(IOrderDao orderDao) {
this.orderDao = orderDao;
}
4 Spring 概念
4.1 Spring 解惑 @RunWith的作用
@RunWith就是一个运行器
@RunWith(JUnit4.class)就是指用JUnit4来运行
4.2 Spring 解惑 spring-xml配置文件编译
问题:maven项目中,以前编写spring-xml总是不编译。
解决方案共有两种:
1 将配置文件xml放在main文件夹下的resource文件夹,和java文件夹平级。
2 右键项目==》build path==》config buildpath==》 source==》add folder==》添加xml所在resource文件夹==》ok确认即可
4.3 dao、domain、service、web 分别是java里面的什么概念?为什么总放在一起
web-->domain-->service-->dao
这是mvc结构, web主要是 客户端网页,是表层的东西,可接收和返回数据给用户
domain主控制层,是用户与数据库交互的核心中转站,控制用户数据收集,控制请求转向
service是
业务逻辑层,处理数据
逻辑,验证数据,
dao是持久层,读写数据库
web发出请求---domain接收控制数据转向(可以返回也可以进入service)---service验证数据正确性或者是否符合业务要求---dao存入,读出,依次返回
4.4 DO、PO、BO、DTO、VO等概念
- POJO 全称为:Plain Ordinary Java Object,普通的 java 对象,一般用在数据层映射到数据库表的类,类的属性与表字段一一对应
- PO 全称为:Persistant Object,持久化对象,与数据库结构映射的实体,数据库中的一条数据即为一个 PO 对象
- BO 全称为:Business Object,业务对象,主要作用是把业务逻辑封装成一个对象,这个对象可以包括一个或多个其它对象,比如一个简历 BO 中有教育经历,工作经历,社会关系等 BO 对象
- DTO 全称为:Data Transfer Object,数据传输对象,比如一张表有 100 个字段,那么对应的 PO 就有100 个属性(大多数情况下,DTO内部的数据结构来自多个表)但 view 层只需要显示 10 个字段,没有必要把整个 PO 对象传递到 client,这时我们就可以用只有这 10 个属性的 DTO 来传输给 client,这样也不会暴露 server 端表结构,到达客户端后,如果这个对象来对应页面显示,它的身份就转为 VO
- VO 全称为:View Object,主要对应页面展示的数据对象,一般继承自 PO,可以添加 PO 中没有的字段,用来逻辑处理以及其它消息存储
- DO 全称为:Domain Object,领域对象,从现实世界中抽象出的业务实体,一般还包含 ORM 映射
- DAO 全称为:Data Access Object,数据访问对象,一般所说的 DAO 层,用于连接数据库与外层之间的桥梁,并持久化数据层对象
- JavaBean 是一种 Java 语言写成的可重用组建,它的规范必须符合特定的约定:
- 这个类必须有一个公共的缺省构造函数
- 这个类的属性用 getter 和 setter 访问
- 这个类可被序列化
模型
- 用户发出请求,表单的数据层被匹配为 VO
- 展示层把 VO 转换为服务层对应方法锁要求的 DTO,传输给服务层
- 服务层首先根据 DTO 的数据构造一个 DO,调用 DO 的业务方法完成具体业务
- 服务层把 DO 转换为持久层对应的 PO,调用持久层持久化方法,把 PO 传递给它完成持久化操作
转载:https://www.jianshu.com/p/1832575fd79e
浙公网安备 33010602011771号