高内聚低耦合,是软件工程中的概念,是判断软件设计好坏的标准,主要用于程序的面向对象的设计,主要看类的内聚性是否高,耦合度是否低。目的是使程序模块的可重用性、移植性大大增强。通常程序结构中各模块的内聚程度越高,模块间的耦合程度就越低。
内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事,它描述的是模块内的功能联系;
耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。
Tips:元素可以是方法、对象(类),也可以指系统、子系统、模块、服务等。
耦合,简单的来说就是元素(类)与元素(类)之间的关系;我们在设计程序时应该降低元素与元素之间的直接关系;降低元素与元素之间的耦合性;
假如一个元素A去调用元素B,或者通过自己的方法可以感知B,当B不存在的时候就不能正常工作,那么就说元素A与元素B耦合。耦合带来的问题是,当元素B发生变更或不存在时,都将影响元素A的正常工作,影响系统的可维护性和易变更性。同时元素A只能工作于元素B存在的环境中,这也降低了元素A的可复用性。
简单的来说就是元素A不能过度依赖元素B;
1)定制合理的职责划分,让系统中的对象各司其职,不仅是提高内聚的要求,同时也可以有效地降低耦合;
2)使用接口而不是继承:我们不难发现。继承就是一种耦合,假如子类A继承了父类B,父类B修改了任何功能将直接影响到子类,而接口则是将功能延迟到了子类中来实现;
架构设计时的内聚高低是指,设计某个模块时,模块内部的一系列相关功能的相关程度的高低。相关程度越高,我们称之为高内聚,反之低内聚;内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。
很明显,程序的内聚性越高,代表功能的相关性也就越高,从一定程度上来说,这个功能就应该属性这个元素;因此耦合性就降低了;
程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。
高内聚低耦合只是一个度的问题,我们并不能做到完全的高内聚低耦合;有时候太过严格反而会适得其反
我们可以举几个代码上的例子:
1)我们的UserServlet中,出现了UserService和DeptService,从一定程度上来讲,Dept的业务应该是不属于我User管辖的
如果要严格按照"高内聚,低耦合"的要求来,那么只能把DeptService的业务写在DeptServlet中,这样做虽然是降低了UserServlet与DeptService的联系,提高了UserServlet的内聚,但很显然,UserService变得非常臃肿(高耦合、低内聚)
2)我们再"严格"一点,ArticleServlet的功能应该由ArticleServlet来提供,而不需要借助其他类,否则ArticleServlet就与ArticleService产生耦合关系:
那么为了进一步降低耦合性,只能把ArticleService的代码编写到ArticleServlet中,那么很显然,ArticleSerlvet的内聚性就非常低了,因为很多不是属于ArticleServlet的代码也编写到ArticleServlet中了;
因此"高内聚低耦合只是一个度的问题",但是实际的设计开发过程中,总会发生这样那样的问题与情况,真正做到高内聚、低耦合是很难的,很多时候未必一定要这样, 更多的时候“最适合”的才是最好的, 不过,理解思想,审时度势地使用, 融会贯通,灵活运用,才是设计的王道。
比如说我们设计一个类可以不与JDK耦合,这可能吗?除非你不是设计的Java程序。再比如我设计了一个类,它不与我的系统中的任何类发生耦合。如果有这样一个类,那么它必然是低内聚,而且是非常低的内聚(因为所有的功能都在一个类的,耦合非常高,内聚非常低)。耦合与内聚常常是一个矛盾的两个方面。最佳的方案就是寻找一个合适的中间点。
高内聚:一个的元素中的功能应该都是相关程度非常高的,不要提供与本身无关的功能
低耦合:尽量与其他元素不要产生关系;
Spring是一个JavaEE轻量级的一站式开发框架。
JavaEE: 就是用于开发B/S的程序。(企业级)
轻量级:使用最少代码启动框架,然后根据你的需求选择,选择你喜欢的模块使用。
一站式:提供了表示层,服务层,持久层的所有支持。
在世界第一套有Java官方Sun公司推出的企业级开发框架EJB出现后,瞬间风靡全球。被各大公司所应用。Spring之父,Rod Jonhson是一个音乐博士,在Sun公司的大力推广下,也成为EJB框架的使用者。
在深入研究完EJB框架(由Sun主导开发的一个JavaEE开发框架),无法接收这么一个框架被吹成世界第一,其中突出被他吐槽最厉害的一个点就EJB的重量级,就是只要使用EJB里面任何一个组件。都要将所有EJB的jar导进去。
于是他就提供了一个他的解决方案:**轻量级的一站式企业开发框架。**那么什么是轻量级呢?就是除内核模块(4个jar),其他模块由开发者自由选择使用,同时支持整合其他框架。也可以称为就是可插拔式开发框架,像插头和插座一样,插上就用。这就是Spring框架核心理念。
那么什么是一站式呢?
就是Spring框架提供涵盖了JavaEE开发的表示层,服务层,持久层的所有组件功能。也就是说,原则上,学完一套Spring框架,不用其他框架就可以完成网站一条流程的开发。
Spring提供的功能模块特别多,我们需要什么功能就导入对应的jar包即可,但是Spring环境有4个核心包必须导入;
包名 | 说明 |
---|---|
spring-aop-5.2.9.RELEASE.jar | 实现了AOP的支持 |
spring-aspects-5.2.9.RELEASE.jar | AOP框架aspects支持包 |
spring-beans-5.2.9.RELEASE.jar | 内核支撑包,实现了处理基于xml对象存取 |
spring-context-5.2.9.RELEASE.jar | 内核支撑包,实现了Spring对象容器 |
spring-context-support-5.2.9.RELEASE.jar | 容器操作扩展包,扩展了一些常用的容器对象的设置功能 |
spring-core-5.2.9.RELEASE.jar | 内核支撑包,Spring的内核 |
spring-expression-5.2.9.RELEASE.jar | 内核支撑包,实现了xml对Spring表达式的支持 |
spring-instrument-5.2.9.RELEASE.jar | 提供了一些类加载的的工具类 |
spring-instrument-tomcat-5.2.9.RELEASE.jar | 提供了一些tomcat类加载的的工具类,实现对应Tomcat服务的调用 |
spring-jdbc-5.2.9.RELEASE.jar | SpringJDBC实现包,一个操作数据库持久层的子框架 |
spring-jms-5.2.9.RELEASE.jar | 集成jms的支持,jms:Java消息服务。 |
spring-messaging-5.2.9.RELEASE.jar | 集成messaging api和消息协议提供支持 |
spring-orm-5.2.9.RELEASE.jar | ORM框架集成包,实现了Hibernate,IBatis,JDO的集成。 |
spring-oxm-5.2.9.RELEASE.jar | Spring OXM对主流O/X Mapping框架做了一个统一的抽象和封装。就是对应XML读写框架的支持 |
spring-test-5.2.9.RELEASE.jar | Spring集成JUnit测试 |
spring-tx-5.2.9.RELEASE.jar | 事务代理的支持 |
spring-web-5.2.9.RELEASE.jar | SpringWeb通用模块 |
spring-webmvc-5.2.9.RELEASE.jar | SpringMVC子框架 |
spring-webmvc-portlet-5.2.9.RELEASE.jar | Spring对门户技术(portlet)的支持 |
spring-websocket-5.2.9.RELEASE.jar | Spring对websocket的支持 |
Tips:我们本次采用的是Spring最新版本5.0
搭建Spring环境最少需要4个核心包,分别是context、core、expression、beans,在Maven中,我们直接导入context的坐标依赖即可
4.0.0 com.dfbz 01_Spring 1.0-SNAPSHOT org.springframework spring-context 5.2.12.RELEASE org.projectlombok lombok 1.18.18 junit junit 4.9 test
在依赖空白处右键:Maven–>Show Dependencies
或者直接按住快捷键
Ctrl+Shift+Alt+U
查看Maven依赖图:
编写功能类:
package com.dfbz.service;/*** @author lscl* @version 1.0* @intro:*/
public class HelloService {public void sayHello(){System.out.println("hello spring!");}
}
package com.dfbz.test;import com.dfbz.service.HelloService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author lscl* @version 1.0* @intro:*/
public class Demo01 {@Testpublic void test1() {// 获取SpringIOC容器ApplicationContext app =new ClassPathXmlApplicationContext("application.xml");// 根据id获取HelloService helloService = (HelloService) app.getBean("helloService");// 根据字节码对象获取
// HelloService helloService = app.getBean(HelloService.class);helloService.sayHello();}
}
通过Spring,我们可以不用new就可以创建对象;对象交给Spring帮我们管理,想要的时候直接去Spring的IOC容器里面获取;
我们来使用三层架构模拟一段保存user的逻辑:
package com.dfbz.dao;/*** @author lscl* @version 1.0* @intro:*/
public interface UserDao {void save();
}
package com.dfbz.dao.impl;import com.dfbz.dao.UserDao;/*** @author lscl* @version 1.0* @intro:*/
public class UserDaoImplMySQL implements UserDao {@Overridepublic void save() {System.out.println("使用MySQL保存到数据库...");}
}
package com.dfbz.dao.impl;import com.dfbz.dao.UserDao;/*** @author lscl* @version 1.0* @intro:*/
public class UserDaoImplOracle implements UserDao {@Overridepublic void save() {System.out.println("使用Oracle保存到数据库..");}
}
package com.dfbz.service;import com.dfbz.dao.UserDao;/*** @author lscl* @version 1.0* @intro:*/
public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}public void save() {userDao.save();}
}
package com.dfbz.controller;import com.dfbz.service.UserService;/*** @author lscl* @version 1.0* @intro:*/
public class UserController {private UserService userService;public UserController(UserService userService) {this.userService = userService;}public void save() {userService.save();}}
我们会发现,要保存一个用户必须手动的创建一个个对象,并将对象移除组装起来,最后组装成一个UserController,然后才可以调用;
@Test
public void test2() {UserDao userDao = new UserDao();UserService userService = new UserService(userDao);UserController userController = new UserController(userService);userController.save();
}
存在的问题:
1)UserController的过程非常复杂且需要我们来负责整个的创建过程,我们必须要对UserController的结构非常清晰才可以创建出来,实际开发中,UserController可能都不是我编写的;甚至整个User的业务我都没有参加,我只是想用一下UserController的某个功能;
2)因为是直接通过new生成的具体对象,这是一种硬编码的方式,违反了面向接口编程的原则,程序间存在严重的耦合,如果有一天我们从MyBatis的Dao切换到Hiberante的Dao那么将会需要改动源代码;
3)每一处使用UserController都需要这么几个步骤,频繁的创建对象,浪费资源;
@Test
public void test4() {// 获取SpringIOC容器ApplicationContext app =new ClassPathXmlApplicationContext("spring.xml");// 从IOC容器中获取一个UserController,具体UserController是如何创建出来的,我不管UserController userController = (UserController) app.getBean("userController");userController.save();
}
使用IOC之后,我们只管向容器索取所需的Bean即可。IOC便解决了以下的问题:
1)Bean之间的解耦,Bean不需要我来创建了,我们也不必对Bean的内部非常了解,只管像IOC容器获取即可;
2)当dao类需要跟换时,只需要在spring的配置文件中更换dao的实现类即可,原来的代码都不行做任何的改动;
3)IOC容器天生支持单例;
控制反转(Inversion of Control,缩写为IOC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
反转意味着我们把对象创建的控制权交出去,交给谁呢?交给IOC容器,由IOC容器负责创建特定对象,并填充到各个声明需要特定对象的地方。
在Spring 容器读取Bean配置创建Bean实例之前,必须对它进行实例化。只有在容器实例化后,才可以从容器里获取Bean实例并使用。
Spring提供了多种类型的容器实现。
BeanFactory
:容器的基本实现。
ApplicationContext
:提供了更多的高级特性,是BeanFactory的子接口。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DFMTTbrX-1669719985321)(media/94.png)]
BeanFactory是Spring 框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory 。
无论使用何种方式,配置文件是相同的。
ConfigurableApplicationContext
扩展于 ApplicationContext,新增加两个主要方法:refresh() 和 close() ,让ApplicationContext具有启动、刷新和关闭上下文的能力。
ClassPathXmlApplicationContext
:通过classpath路径直接获得加载的xml文件(推荐使用)
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
FileSystemXmlApplicationContext
:通过文件路径来获得加载的xml文件。ApplicationContext app = new FileSystemXmlApplicationContext("D:/spring.xml");
AnnotationConfigApplicationContext
:基于配置类注解来创建IOC容器ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfig.class);
我们的Bean都交给了SpringIOC容器来进行管理,意味着Bean的创建由IOC容器来进行,SpringIOC容器提供了多种不同的方式来创建Bean;
@Data
public class Book {private String id;private String name;private Double price;public Book() {System.out.println("book初始化了");}
}
普通创建方式就是之前我们案例中的创建方式
package com.dfbz.factory.factory;import com.dfbz.entity.Book;/*** @author lscl* @version 1.0* @intro: 图书工厂类*/
public class BookFactory {public Book createBook(){return new Book();}
}
package com.dfbz.factory;import com.dfbz.entity.Book;/*** @author lscl* @version 1.0* @intro: 图书工厂类(静态工厂)*/
public class BookStaticFactory {public static Book createBook(){return new Book();}
}
Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。
工厂bean必须实现org.springframework.beans.factory.FactoryBean
接口。
package com.dfbz.factory;import com.dfbz.entity.Book;
import org.springframework.beans.factory.FactoryBean;/*** @author lscl* @version 1.0* @intro:*/
public class BookFactoryBean implements FactoryBean {/*** SpringIOC容器创建bean时调用的方法* @return : 创建的JavaBean* @throws Exception*/@Overridepublic Book getObject() throws Exception {return new Book();}/*** 要创建的JavaBean的类型* @return*/@Overridepublic Class> getObjectType() {return Book.class;}
}
我们前面创建的JavaBean,其属性值默认都是为空的;我们可以通过Spring的方式来给容器中的bean赋值;
通过Bean提供的构造方法来给Bean设置值
public Book(String id, String name, Double price) {System.out.println("有参构造执行了..");this.id = id;this.name = name;this.price = price;
}// 参数顺序不一致
public Book(Double price, String id, String name) {System.out.println("有参构造执行了..");this.id = id;this.name = name;this.price = price;
}
p名称空间:为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。
Spring从2.5版本开始引入了一个新的p命名空间,可以通过
元素属性的方式配置Bean的属性。使用p命名空间后,基于XML的配置方式将进一步简化。
与带有p命名空间的XML快捷方式类似,Spring 3.1中引入的c命名空间允许内联属性来配置构造函数参数
级联赋值:有时候我们bean中的属性的类型并不是一个基本数据类型,而是一个对象,那么如何给这个对象中的某个属性进行赋值呢?
@Data
public class Emp {private String id;private String name;// 所属部门private Dept dept = new Dept();
}
@Data
public class Dept {private String id;private String name;private String location;
}
在Spring中可以通过一组内置的XML标签来配置集合属性
数组和List配置java.util.List类型的属性,需要指定
标签,在标签里包含一些元素。这些标签可以通过指定简单的常量值,通过指定对其他Bean的引用。通过
指定内置bean定义。通过
指定空元素。甚至可以内嵌其
他集合。
数组的定义和List一样,都使用
元素。
配置java.util.Set需要使用
标签,定义的方法与List一样。
// 部门员工
private List empList = new ArrayList<>();// 曾用名
private List usedName=new ArrayList<>();
技术部 开发部
@Data
public class Pojo {private Map map=new HashMap();private Properties props=new Properties();
}
001 《尚书》 39.8
Spring IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。
Spring IOC容器对bean的生命周期进行管理的过程:
1)默认情况下,在容器启动时,Spring会将所有的bean都初始化
2)为bean的属性设置值(通过构造方法、set方法等)
3)调用bean的初始化方法init
4)bean可以使用了
5)默认情况下,当容器关闭时,Spring会将所有的bean销毁
在配置bean时,通过bean标签的init-method
和destroy-method
属性为bean指定初始化和销毁方法
@Data
public class Person {private String name;private Integer age;private String addr;public void init(){System.out.println("初始化了");}public void destroy(){System.out.println("销毁了");}public Person() {System.out.println("person创建了");}
}
package com.dfbz.test;import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author lscl* @version 1.0* @intro:*/
public class Demo02 {//获取SpringIOC容器ConfigurableApplicationContext app =new ClassPathXmlApplicationContext("spring2.xml");@Testpublic void test1() {Object person = app.getBean("person");System.out.println(person);app.close();}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V2uC9wIX-1669719985322)(media/91.png)]
默认情况下,IOC容器中的bean都是单例的,而且是容器启动时,会创建所有的bean;
@Test
public void test2() {Object person = app.getBean("person");Object person2 = app.getBean("person");System.out.println(person == person2); // true,同一个bean只会初始化一份
}
可以通过lazy-init属性来调整加载的时机,等到用到的时候再加载:
可以通过scpoe属性来调整bean的作用范围:
@Test
public void test2() {Object person = app.getBean("person");Object person2 = app.getBean("person");System.out.println(person == person2); //falseapp.close(); // 容器关闭时,多实例的bean不会随之销毁
}
注意:在多例中,容器创建时不会加载多实例的bean,而是等到具体用的时候再加载,每次获取的实例都是一个全新的实例,并且IOC容器销毁时,多实例的bean也不会随之销毁;而是将Bean的销毁交给了JVM的垃圾回收
Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理
bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor
package com.dfbz.process;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;/*** @author lscl* @version 1.0* @intro:*/
public class MyProcess implements BeanPostProcessor {/*** 创建完对象后执行前置方法* @param bean: 要创建出来的bean* @param beanName: bean的名称* @return* @throws BeansException*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("【"+beanName+"】执行了前置方法,类型为: "+ bean.getClass());return bean;}/*** 对象的init方法执行完再执行后置方法* @param bean: 要创建出来的bean* @param beanName: bean的名称* @return* @throws BeansException*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("【"+beanName+"】执行了后置方法,类型为: "+ bean.getClass());return bean;}
}
测试效果:
我们习惯性的把一些配置写在外部的xxx.properties
文件中,Spring提供了context命名空间帮我们读取类路径下的配置文件
mysql mysql-connector-java 5.1.47
com.alibaba druid 1.2.1
jdbc.username=root
jdbc.password=admin
jdbc.url=jdbc:mysql://localhost:3306/db01
jdbc.driverClassName=com.mysql.jdbc.Driver
Spring表达式语言全称为:Spring Expression Language,简称SpEL,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等等,并且能与Spring功能完美整合,如能用来配置Bean定义。
和JSP页面上的EL表达式、Struts2中用到的OGNL表达式一样,SpEL根据JavaBean风格的getXxx()、setXxx()方法定义的属性访问对象图,完全符合我们熟悉的操作习惯。
整数:
小数:
科学计数法:
String类型的字面量可以使用单引号或者双引号作为字符串的定界符号
或者
布尔类型:
引用其他bean:
Utils类:
package com.dfbz.util;import java.util.Random;
import java.util.UUID;/*** @author lscl* @version 1.0* @intro:*/
public class Utils {/*** 使用普通方法生成id** @return*/public String getId() {return UUID.randomUUID().toString();}/*** 使用静态方法生成价格* @return*/public static Double getPrice() {return Double.parseDouble((new Random().nextDouble() * 100 + "").substring(0, 4));}
}
spring.xml:
60}"/>
60 | 30<80}"/>
60? '30大于60啊':'30小于60啊'}"/>
上一篇:PG::Vegeta1
下一篇:【遥感图像:信息增强】