一、Spring
Spring是一个为了解决企业应用程序开发复杂性而创建的开源框架,其核心是IOC–控制反转、AOP–面向切面编程。
框架的主要优势之一就是其分层架构(WEB层(springMvc)、业务层(Ioc)、持久层(jdbcTemplate)),分层架构允许选择使用哪一个组件,同时为J2EE(Java 2 Platform Enterprise EditionJava 2 平台企业版)应用程序开发提供集成的框架。
Spring是非侵入性的,这意味着业务逻辑代码可以不依赖于框架本身,开发人员可以专注于业务功能的实现,而不必关心框架内部的处理逻辑。
Spring 与 Spring Boot的区别
SpringBoot就是一个轻量级,简化配置和开发流程的web整合框架。
下面讨论Spring和Spring Boot之间的主要区别:
Spring Boot可以建立独立的Spring应用程序;
内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说可以直接跑起来,用不着再做部署工作了;
无需再像Spring那样搞一堆繁琐的xml文件的配置;
可以自动配置(核心)Spring。SpringBoot将原有的XML配置改为Java配置,将bean注入改为使用注解注入的方式(@Autowire),并将多个xml、properties配置浓缩在一个appliaction.yml配置文件中。
提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功能;
整合常用依赖(开发库,例如spring-webmvc、jackson-json、validation-api和tomcat等),提供的POM可以简化Maven的配置。当我们引入核心依赖时,SpringBoot会自引入其他依赖。
Spring | Spring Boot |
Spring Framework是用于构建应用程序的广泛使用的Java EE框架。 | Spring Boot Framework被广泛用于开发REST API。 |
它旨在简化Java EE开发,从而使开发人员更加高效。 | 它旨在缩短代码长度,并提供最简单的方法来开发Web应用程序。 |
Spring Framework的主要功能是依赖项注入。 | Spring Boot的主要功能是自动配置,它会根据需求自动配置类。 |
通过允许我们开发松耦合应用程序,它可以使事情变得更简单。 | 它有助于创建配置更少的独立应用程序。 |
开发人员编写了大量代码(样板代码)来完成最小的任务。 | 它减少了样板代码。 |
为了测试Spring项目,需要显式设置服务器。 | Spring Boot提供了Jetty和Tomcat等嵌入式服务器。 |
它不提供对内存数据库的支持。 | 它提供了几个插件来处理嵌入式和内存数据库(例如H2)。 |
开发人员在pom.xml中手动定义Spring项目的依赖项。 Spring Boot在pom.xml文件中带有启动程序的概念,该文件在内部负责根据Spring Boot Requirement下载依赖项JAR。 |
Spring Boot与Spring MVC的区别
Spring MVC是Spring基础之上的一个MVC模式的WEB开发框架,涵盖面包括前端视图开发、文件配置、后台接口逻辑开发等,XML、config等配置相对比较复杂。
SpringBoot框架相对于SpringMVC框架来说,更专注于开发微服务后台接口,不开发前端视图,同时遵循默认优于配置,简化了插件配置流程,不需要配置xml,相对Springmvc,大大简化了配置流程。
Spring Boot和Spring MVC出于不同的目的而存在。 下面讨论了Spring Boot和Spring MVC之间的主要区别:
Spring Boot | Spring MVC |
Spring Boot是Spring的模块,用于使用合理的默认值打包基于Spring的应用程序。 | Spring MVC是Spring框架下基于模型视图控制器的Web框架。 |
它提供了默认配置来构建Spring支持的框架。 | 它提供了用于构建Web应用程序的即用型功能。 |
无需手动构建配置。 | 它需要手动进行构建配置。 |
不需要部署描述符。 | 部署描述符是必需的。 |
它避免了样板代码,并将依赖项包装在一个单元中。 | 它分别指定每个依赖项。 |
它减少了开发时间并提高了生产率。 | 实现相同目的需要更多时间。 |
总结:
1、Spring框架就像一个家族,有众多衍生产品例如:boot、security、jpa等等。但它们的基础都是Spring的ioc、aop等;ioc提供了依赖注入的容器,aop解决了面向横切面编程,然后在此两者的基础上实现了其他产品的高级功能;
2、SpringMVC主要解决WEB开发的问题,是基于Servlet的一个MVC框架,通过XML配置,统一开发前端视图和后端逻辑。
由于Spring的配置非常复杂,各种XML、JavaConfig、Servlet处理起来比较繁琐,为了简化开发者的使用,从而创造性地推出SpringBoot框架,默认优于配置,简化了SpringMVC的配置流程;但区别于SpringMVC的是,SpringBoot专注于单体微服务接口开发,和前端解耦,虽然SpringBoot也可以做成SpringMVC前后台一起开发,但是这就有点不符合springBoot框架的初衷了。
https://www.yiibai.com/spring-boot/spring_boot_introduction.html
Spring框架优点?
1、方便解耦、简化开发
Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护工作都交给Spring容器管理,大大的降低了组件之间的耦合性。
2、支持AOP
Spring提供了对AOP的支持,它允许将一些通用任务,如安全、事务、日志等进行集中式处理,从而提高了程序的复用性。
3、支持声明式事物管理
方便对程序进行声明式事物管理,无需手动编程,提高开发效率和质量。
4、方便程序的测试
Spring提供了对Junit4的支持,可以通过注解方便的测试Spring程序。
5、方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持。
6、降低Java EE API的使用难度
Spring对Java EE开发中非常难用的一些API(如:JDBC、JavaMail等),都提供了封装,使这些API应用难度大大降低。
7、非侵入式框架
Spring是一种非侵入式(non-invasive)框架,能减少应用程序对框架的依赖。
谈谈你对Spring IOC和DI的理解,它们有什么区别?
Spring IOC全面认知:https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484247&idx=1&sn=e228e29e344559e469ac3ecfa9715217&chksm=ebd74256dca0cb40059f3f627fc9450f916c1e1b39ba741842d91774f5bb7f518063e5acf5a0&scene=21##wechat_redirect
IoC(思想,设计模式)主要的实现方式有两种:依赖查找,依赖注入。
依赖注入是一种更可取的方式(实现的方式)。
IOC控制反转:将对象的创建,查找依赖,以及生命周期的控制权交给了Ioc容器。对象之间耦合较松,更加灵活。
依赖注入跟控制反转其实是从不同的方向表达同一种思想,依赖注入说的是对象的获取需要依赖spring ioc容器,当需要处理对象间的依赖关系时,spirng ioc容器会将需要的对象依赖关系注入到相应的对象中,不需要我们主动去创建对象。
依赖注入(DI),Spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。
IoC 具体的实现方式是依赖注入。
简单的说之前我们在代码中创建一个对象是通过 new 关键字,而使用了 Spring 之后,我们不在需要自己去 new 一个对象了,而是直接通过容器里面去取出来,再将其自动注入到我们需要的对象之中,也就说创建对象的控制权不在我们程序员手上了,全部交由 Spring 进行管理。
交给 Spring 管理的也称为 Bean,所有的 Bean 都被存储在一个 Map 集合中,这个 Map 集合也称为 IoC 容器。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
比如说,在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能每次都要搞清这个 Service 所有底层类的构造函数,这显然过于繁琐。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
什么是AOP,AOP 的作用是什么?
面向切面编程(Aspect Oriented Programming(AOP)),是一个比较热门的话题。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
切面,指的是项目模块中某些业务逻辑。面向切面编程通俗一点就是面向业务的一种编程思想(当然业务需要一定的共性),然后通过代理完成类的创建,极大程度的降低代码的重复,有效的提高了代码的重用率和开发效率。
面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用CGLIB 方式实现动态代理。
Spring里面如何配置数据库驱动?
“org.springframework.jdbc.datasource.DriverManagerDataSource”数据源来配置数据库驱动。
示例如下:
<bean id=”dataSource”>
<property name=”driverClassName”>
<value>org.hsqldb.jdbcDriver</value>
</property>
<property name=”url”>
<value>jdbc:hsqldb:db/appfuse</value>
</property>
<property name=”username”><value>sa</value></property>
<property name=”password”><value></value></property>
</bean>
JDBC注册驱动方式
1、Class.forName("com.mysql.cj.jdbc.Driver");通过Class把类先装载到java的虚拟机中,并没有创建Driver类的实例。
最常见的注册驱动方式,也是推荐的注册方式,参数使用字符串,所以移植性好。
好处在于不依赖指定的驱动,也就减少了代码的依赖性,可以更改为在配置文件中配置,从而动态更新数据库驱动。
2、System.setProperty(“jdbc.drivers”,”com.mysql.cj.jdbc.Driver”);通过系统的属性设置注册驱动。
可以同时注册多个驱动,如System.setProperty("jdbc.drivers","com.mysql.jdbc.Driver:com.Oracle.jdbc.Driver");
这种驱动注册方式很少使用。
3、实例化new com.mysql.jdbc.Driver();
这样的话我们不需要使用DriverManager.registerDriver(new Driver())去注册驱动,因为在com.mysql.cj.jdbc.Driver的静态代码块中已经帮我们完成了这样的操作。
数据连接池的工作机制是什么?
因为我们在操作数据库的时候 ,打开连接和关闭连接是耗时,耗性能,我们希望通过种机制减少这种开销,这就是数据连接池的初衷。
连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。
使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理,这样就能提高我们的访问性能。
同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
Spring配置bean实例化有哪些方式?
1.使用类构造器实例化
2.使用工厂方法(静态工厂方法、实例工厂方法)
3.实现FactoryBean接口
使用类构造器实例化:
在配置bean时,使用class属性指定类的全名称(即全类名)
工厂方法实例化
1.静态工厂方法:
方法就是将对象创建过程封装到静态方法中,当客户端需要对象时,只需要简单的调用静态方法。
要声明通过静态方法创建的bean,需要在bean的class属性中指定拥有该工厂方法的类,同时在factory-method属性中指定工厂方法的名称,最后使用元素为该方法传递方法参数
静态方法如下所示:
public class FruitFactory {
public static Fruit createApple() {
Fruit fruit = new Fruit();
fruit.setType("apple");
return fruit;
}
public static Fruit createPear() {
Fruit fruit = new Fruit();
fruit.setType("pear");
return fruit;
}
}
bean.xml中的配置如下所示
2.实例工厂方法配置方式:
将对象的创建过程封装到另一个对象实例的方法中。当客户端需要请求对象时,只需要简单的调用该实例方法。
使用方式:
先声明要使用实例化方法创建的bean
在第二个bean的factory-bean属性里指定拥有该工厂方法的bean
在factory-method属性里面指定该工厂方法的名称
实例方法如下所示:
public class FruitFactory {
//实例工厂方法
public Fruit createPeach(){
Fruit fruit = new Fruit();
fruit.setType("Peach");
return fruit;
}
}
bean中的配置如下所示(无参数)
<bean id="fruitFactory" class="anhui.FruitFactory"></bean>
<bean id="peach" factory-bean="fruitFactory" factory-method="createPeach"></bean>
如果有参数,bean中的配置情况如下
<bean id="fruitFactory" class="anhui.FruitFactory"></bean>
<bean id="peach" factory-bean="fruitFactory" factory-method="createPeach">
<constructor-org>index="0" value="peach"</constructor-org>
</bean>
3.使用FactoryBean接口进行实例化
FactoryBean:由Spring提供
getObjectType:返回bean的实例
isSingleton:返回的bean是否是单例的。
配置时:
class指定为上面创建的FactoryBean实现类。实际返回的是FactoryBean的getObject方法返回的实例。
Property:为实现类的property
实现类代码如下所示:
import org.springframework.beans.factory.FactoryBean;
public class FruitFactoryBean implements FactoryBean {
//指定创建对象的方法
public Fruit getObject() throws Exception {
Fruit fruit = new Fruit();
fruit.setType("strawberry");
return fruit;
}
public Class<?> getObjectType() {
return Fruit.class;
}
public boolean isSingleton() {
return true;
}
}
bean中的配置如下所示:
<bean id="strawberry" class="wanho.FruitFactoryBean"></bean>
请介绍一下Spring框架中 Bean 的生命周期和作用域
Bean 的生命周期
1. 实例化 Bean (不等于初始化) 【分配内存空间】
2. 设置属性【依赖注入DI】
3. Bean 的初始化
执行各种通知
初始化的前置方法. (以前是通过在 xml 中配置 init-method() 方法, 之后改用 @PostConstruct 注解)
初始化方法
初始化的后置方法
4. 使用 Bean
5.销毁 Bean. (以前通过 xml 的 destroy-method, 之后改用 @PreDestroy 注解)
Bean 的六大作用域
1. singleton:单例作⽤域
2. prototype:原型作⽤域(也叫多例作⽤域)
3. request:请求作⽤域
4. session:会话作⽤域
5. application:全局作⽤域
6. websocket:HTTP WebSocket 作⽤域
前两种作用域是在普通的 Spring 项目中使用,后四种作用域存在于 Spring MVC 项目中。(前四种要知道)
1.1.1 单例作用域 (singleton)
含义: 单例作用域是指 在 Spring IoC 容器中只存储一份, 也就是说只有一个实例, 无论我们是通过 @Autowried, @Resource 去获取, 还是通过上下文对象去 getBean(), 拿到的 bean 对象都是同一份. (并且单例作用域是 Spring 中默认的作用域)
场景: 通常是无状态的 Bean 使用的作用域. (无状态表示 Bean 对象的属性状态不需要修改)
1.1.2 原型作用域 (prototype),原型作用域也叫作多例作用域
含义: 原型作用域也叫作多例作用域, 每次从 Spring 中获取 Bean 对象, 都会创建一份新的实例, @Autowired, @Resource 注入的对象以及 context 上下文 getBean 拿到的都是不同的 bean 对象。
场景: 通常是有状态的 Bean 使用的作用域 (有状态表示 Bean 对象的属性需要被修改)
1.1.3 请求作用域 (request)
含义: 每一次 HTTP 请求都会创建新的实例, 类似于 prototype.
场景: 一次 HTTP 的请求和响应共享一个 bean。(仅在 Spring MVC 中使用)
1.1.4 会话作用于 (session)
含义: 在一个 HTTP session 中, 定义一个 Bean 实例.
场景: 同一个用户的会话共享 Bean (例如在登录场景中记录一个用户的登录信息) 仅在 Spring MVC 中使用
1.1.5 全局作用于 (application)
含义: 在一个 HTTP Servlet Context 中, 定义一个 Bean 实例
场景: Web 应用的上下文信息, 记录一个应用的共享信息.(仅在 Spring MVC 中使用)
application 作用域和 单例作用域还是有区别的, 它只是同一份上下文对象共享同一个 bean, 当再次创建上下文对象时, 调用 getBean() 就是另一个 Bean 对象了.
1.1.6 HTTP WebSocket 作用域 (websocket)
含义: 在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
场景: WebSocket的每次会话中,保存了⼀个 Map 结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。(仅在 Spring MVC 中使用)
如何设置Bean的作用域
在Spring中,可以在元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。Scope属性有四个参数(singleton:单例作⽤域、prototype:原型作⽤域(也叫多例作⽤域)、request:请求作⽤域、session:会话作⽤域)
具体的使用可以看下图:
<!-- singleton单实例bean
1、在容器创建时被创建
2、只有一个实例
-->
<bean id="book02" class="com.spring.beans.Book" scope="singleton"></bean>
设置 Bean 的作用域有两种方式:
直接设置: @Scope("prototype")
使用类似枚举的方式设置: @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
1.2.1 直接设置: @Scope("prototype")
@Controller
public class CatBean {
@Scope("prototype")
@Bean
public Cat cat() {
Cat cat = new Cat();
cat.setId(1);
cat.setName("加菲猫");
cat.setAge(12);
return cat;
}
}
我们只需要在前面的代码中的 CatBean 类中的 @Bean 注解上加上一个 @Scope 注解, 并设置 "prototype" , 此时我们运行程序, 如果李四拿到的是 "加菲猫", 那么就说明此时是多例作用域。
Bean注入属性有哪几种方式?
构造器注入:利用构造方法的参数注入依赖。
在实体类中添加两个构造方法:有参和无参
//默认构造函数
public Person(){}
//带参构造函数
public Person(Long pid,Student students){
this.pid = pid;
this.students = students;
}
在 applicationContext.xml 中进行赋值
1、如果Spring的配置文件中的bean中没有<constructor-arg>该元素,则调用默认的构造函数
2、如果Spring的配置文件中的bean中有<constructor-arg>该元素,则该元素确定唯一的构造函数
<!-- 根据构造函数赋值 -->
<!--
index 代表参数的位置 从0开始计算
type 指的是参数的类型,在有多个构造函数时,可以用type来区分,要是能确定是那个构造函数,可以不用写type
value 给基本类型赋值
ref 给引用类型赋值
-->
<bean id="person_con" class="com.ys.di.Person">
<constructor-arg index="0" type="java.lang.Long" value="1">
</constructor-arg>
<constructor-arg index="1" type="com.ys.di.Student" ref="student_con"></constructor-arg>
</bean>
<bean id="student_con" class="com.ys.di.Student"></bean>
Setter注入:调用Setter的方法注入依赖。
创建实体类 为实体类里的属性创建set get方法
在 applicationContext.xml 中进行赋值
如果需要使用set注入,那么必须要为属性提供set方法,Spring容器就是通过调用bean的set方法为属性注入值的。而在xml文件中,使用set注入的方式就是通过property标签,如下所示:
<!-- 定义car这个bean,id为myCar -->
<bean id="myCar" class="cn.tewuyiang.pojo.Car">
<!--
为car的属性注入值,因为speed和price都是基本数据类型,所以使用value为属性设置值;
注意,这里的name为speed和price,不是因为属性名就是speed和price,
而是set方法分别为setSpeed和setPrice,名称是通过将set删除,然后将第一个字母变小写得出;
-->
<property name="speed" value="100"/>
<property name="price" value="99999.9"/>
</bean>
<!-- 定义user这个bean -->
<bean id="user" class="cn.tewuyiang.pojo.User">
<property name="name" value="aaa" />
<property name="age" value="123" />
<!-- car是引用类型,所以这里使用ref为其注入值,注入的就是上面定义的myCar
基本数据类型或Java包装类型使用value,
而引用类型使用ref,引用另外一个bean的id
-->
<property name="car" ref="myCar" />
</bean>
字段注入:在字段上使用@Autowired/Resource注解。
构造器注入:强依赖性(即必须使用此依赖),不变性(各依赖不会经常变动)
Setter注入:可选(没有此依赖也可以工作),可变(依赖会经常变动)
Field注入:大多数情况下尽量少使用字段注入,一定要使用的话, @Resource相对@Autowired对IoC容器的耦合更低。
Field注入的缺点
不能像构造器那样注入不可变的对象
依赖对外部不可见,外界可以看到构造器和setter,但无法看到私有字段,自然无法了解所需依赖
会导致组件与IoC容器紧耦合(这是最重要的原因,离开了IoC容器去使用组件,在注入依赖时就会十分困难)
导致单元测试也必须使用IoC容器,原因同上
依赖过多时不够明显,比如我需要10个依赖,用构造器注入就会显得庞大,这时候应该考虑一下此组件是不是违反了单一职责原则
介绍一下Spring的事务管理
Spring事务可以分为两种:
编程式事务(通过代码的方式来实现事务)
声明式事务(通过配置的方式来实现事务),推荐使用(代码侵入性最小),实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多)。
编程式事务在Spring实现相对简单一些,而声明式事务因为封装了大量的东西(一般我们使用简单,里头都非常复杂),所以声明式事务实现要难得多。
在编程式事务中有以下几个重要的了接口:
TransactionDefinition:定义了Spring兼容的事务属性(比如事务隔离级别、事务传播、事务超时、是否只读状态)
TransactionStatus:代表了事务的具体运行状态(获取事务运行状态的信息,也可以通过该接口间接回滚事务等操作)
PlatformTransactionManager:事务管理器接口(定义了一组行为,具体实现交由不同的持久化框架来完成---类比JDBC)
@Autowired和@Resource 之间的区别
@Resource、@Autowired都是Spring中用于实现Bean的依赖注入。
@Autowired是由Spring提供的,根据类型注入,默认情况下要求依赖对象必须存在,如果允许null值,可以设置require属性为false。当存在多个相同类型的bean时 可以通过@Primary注解或者@Qualifier注解解决依赖注入冲突。
@Resource是由Java提供的,默认根据name注入,当找不到与名称匹配的bean才会按照类型装配。
通知有哪些类型?
前置通知:在目标方法执行之前执行的通知
后置通知:在目标方法执行之后的通知。
环绕通知:在目标方法执行之前和之后都可以执行额外代码的通知。
异常通知:在目标方法抛出异常时执行的通知
最终通知:在目标方法执行之后执行的通知。和后置通知不同的是,后置通知是在方法正常返回后执行的通知,如果方法没有正常返回,比如说抛出异常,则后置通知不会执行。而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。另外,后置通知可以通过配置得到返回值,而最终通知无法得到。
二、SpringMVC
l.SpringMVC框架是什么?常见的 mvc 框架有哪些?他们的区别是什么?
Model. Veiw Cantnl
2.阐述一下SpringMVC的运行流程
3.Spring MVC的主要组件有哪些 他们的作用是什么?
4.SpringMVC 怎么样设定重定向和转发的?默认是请求转发还是重定向?
5.SpringMvc 怎么和AJAX 相互调用的?
6.SpringMvc 中如何解决 POST 请求中文乱码问题,GET 的又如何处理呢?
7.SpringMvc如何做异常处理 ?
9.SpringMvc常用的注解有哪些?
10.SpringMvc中函数的返回值是什么?
三、Shiro
1.说一下什么是 rbac,设计一个基于 rbac 的数据库模型,并解释每个模型之间的关系
2.说一下什么是 shiro,并且说出 shiro的优点
3.简述Shiro的核心组件
4、简述Shiro认证过程
5、简述Shiro授权过程
四、Maven
1.Maven 有哪些优点和缺点
优点:
是一个模块化的项目构建工具,方便维护与发布。
不需要手工找jar包,去下载。
缺点:
由于需要下载,导入等,导致加载缓慢,或者出现错误,和不稳定
缺少对Maven的文档描述(网上一大堆,但是没有标准)
中央存储库中的元数据不佳
POM.xml做为项目元数据的来源
2.Maven 坐标由什么组成,分别代表什么意思
那 Maven 是通过什么方式精确地找到用户想要的构件呢?
通过构件的坐标去唯一定位查找。反过来也就是说,在 Maven 仓库中,是用坐标标记来一一对应地管理每个构件的。
那坐标又是由哪些信息组成的呢?
一个完整的坐标信息,由 groupId、artifactId、version、packaging、classifier 组成。
如下是一个简单的坐标定义。
org.SpringFramework
spring-core
4.2.7.RELEASE
jar
这是 JUnit 的坐标,下面详细介绍一下各个元素。
1、groupId定义当前 Maven 项目从属的实际项目。
关于 groupId 的理解如下所示。
1)Maven 项目和实际项目不一定是一一对应的。比如 SpringFramework,它对应的 Maven 项目就有很多,如 spring-core、spring-context、spring-security 等。造成这样的原因是模块的概念,所以一个实际项目经常会被划分成很多模块。
2)groupId 不应该同开发项目的公司或组织对应。原因比较好理解,一个公司和一个组织会开发很多实际项目,如果用 groupId 对应公司和组织,那 artifactId 就只能是对应于每个实际项目了,而再往下的模块就没法描述了,而往往项目中的每个模块是以单独的形式形成构件,以便其他项目重复聚合使用。
3)groupId 的表述形式同 Java 包名的表述方式类似,通常与域名反向一一对应。
2、 artifactId定义实际项目中的一个 Maven 项目(实际项目中的一个模块)。
推荐命名的方式为:实际项目名称-模块名称。
比如,org.springframework 是实际项目名称,而现在用的是其中的核心模块,它的 artifactId 为 spring-core。
3、version定义 Maven 当前所处的版本。
如上的描述,用的是 4.2.7.RELEASE 版本。需要注意的是,Maven 中对版本号的定义是有一套规范的。具体规范请参考《版本管理》的介绍。
4、packaging定义 Maven 项目的打包方式。
打包方式通常与所生成的构件文件的扩展名对应,比如,.jar、.ear、.war、.pom 等。另外,打包方式是与工程构建的生命周期对应的。比如,jar 打包与 war 打包使用的命令是不相同的。最后需要注意的是,可以不指定 packaging,这时候 Maven 会自动默认成 jar。
5、classifier定义构件输出的附属构件。
附属构件同主构件是一一对应的,比如上面的 spring-core-4.2.7.RELEASE.jar 是 spring-core Maven spring-core 项目的主构。
Maven spring-core 项目除了可以生成上面的主构件外,也可以生成 spring-core-4.2.7.RELEASE-javadoc.java 和 spring-core-4.2.7.RELEASE-sources.jar 这样的附属构件。这时候,javadoc 和 sources 就是这两个附属构件的 classifier。这样就为主构件的每个附属构件也定义了一个唯一的坐标。
最后需要特别注意的是,不能直接定义一个 Maven 项目的 classifier,因为附属构件不是由 Maven 项目构建的时候直接默认生成的,而是由附加的其他插件生成的。
groupId、artifactId 和 version 是必需的,packaging 是可选的,默认是 jar,而 classifier 是不能直接定义的。同时,Maven 项目的构件文件名与坐标也是有对应关系的,一般规则是 artifactId-version[-classifier].packaging 。
3.pom 文件中常见的标签有哪些,分别代表什么意思
name标签:项目的名称。
version标签:项目的版本号,项目打包后的JAR文件的版本号跟这里对应。
url:指定项目站点,通常用于maven产生的文档中。
description:描述此项目,通常用于maven产生的文档中。
properties:pom文件中的配置信息,可以配置全局变量。
dependencies:依赖配置集,里面可以添加需要的jar的依赖信息。
dependencyManagement标签: 依赖管理标签。
plugins标签:使用的插件列表。
modelVersion标签:声明项目描述符遵循哪一个POM模型版本。模型本身的版本很少改变,虽然如此,但它仍然是必不可少的,这是为了当Maven引入了新的特性或者其他模型变更的时候,确保稳定性。
groupId标签:项目的全球唯一标识符,通常使用全限定的包名区分该项目和其他项目。并且构建时生成的路径也是由此生成, 如com.mycompany.app生成的相对路径为:/com/mycompany/app。
artifactId标签:构件的标识符,它和group ID一起唯一标识一个构件。换句话说,你不能有两个不同的项目拥有同样的artifact ID和groupID;在某个 特定的group ID下,artifact ID也必须是唯一的。构件是项目产生的或使用的一个东西,Maven为项目产生的构件包括:JARs,源 码,二进制发布和WARs等。
parent标签:父项目的坐标。如果项目中没有规定某个元素的值,那么父项目中的对应值即为项目的默认值。 坐标包括group ID,artifact ID和 version。
packaging标签:项目产生的构件类型,例如jar、war、ear、pom。插件可以创建他们自己的构件类型,所以前面列的不是全部构件类型。
4.常见的Maven私服的仓库类型有哪些
(宿主仓库)hosted+repositor,(代理仓库)proxy+repository,(仓库组)group+repository。
hosted本地仓库:通常我们会部署自己的构件到这一类型的仓库。
代理仓库作用:
(1)作为仓库作用——可以把常用的jar包拷进去,这样客户端就可以从这里下载了。
(2)作为代理的作用——客户端先从私服下载,若私服没有该jar,客户会通过互联网从中央库下载,同时私服也会将jar从中央库下载到私服,这样下次客户端就可以从私服的代理库中下载。
group:仓库组,用来合并多个hosted/proxy仓库。如下图public Repositories的Configuration标签页下Ordered Group Repositories包含了四个仓库:Releases、Snapshots、3rd party和Central。也就是说我们在pom.xml引用这个仓库组,其包含的仓库也被引用。
5.Maven项目的关系有哪些?
依赖关系:A依赖B
继承关系:B和C的公共依赖、部分依赖被A统一管理
聚合关系:B、C功能模块开发完,被A统一进行聚合成完整的功能或项目。 (聚合关系建立在继承关系上)
1. 依赖关系
1.1 标签<dependency>把另一个项目的 jar 引入到当过前的项目
1.2 自动下载另一个项目所依赖的其他项目
2. 继承关系
2.1 父项目是 pom 类型 。
2.2 子项目jar或war,如果子项目还是其他项目的父项目,子项目也是 pom 类型。
2.3 有继承关系后,子项目中出现<parent>标签 。
如果子项目和<groupId>和<version>与父项目项目,在子项目中可以不配置<groupId>和<version>
2.4 父项目 pom.xml 中是看不到有哪些子项目,在逻辑上具有父子项目的关系。
<parent>
<groupId>com.bjsxt</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
3. 聚合关系
3.1 前提是继承关系,父项目会把子项目包含到父项目中。
3.2 子项目的类型必须是 Maven Module, 而不是 maven project 。
3.3 新建聚合项目的子项目时,点击父项目中右键新建 Maven -》 Module
3.4 具有聚合关系的父项目,在 pom.xml 中<modules>
<modules>
<module>child2</module>
</modules>
3.5 具有聚合关系的子项目,在 pom.xml 中<parent>
<parent>
<groupId>com.bjsxt</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
4. 聚合项目和继承项目区别
4.1 在语意上聚合项目父项目和子项目关系性较强
4.2 在语意上单纯继承项目父项目和子项目关系性较弱
5. <dependencyManagement> 写在父项目
5.1 作用:声明可能使用到的所有 jar
5.2 子项目中只需要有坐标的<groupid>和<artifactid>,<version>继承父项目
5.3 在父项目中<properties>把所有版本好进行统一管理
5.4 父项目 pom.xml
5.4.1 <properties>子标签名称自定义
5.4.2 ${名字} 引用标签的值
<properties>
<spring-version>4.1.6.RELEASE</spring-version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
5.5 子项目
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
6.⼀个项目的依赖来源于不同的组织,可能这些依赖还会依赖别的Jar包,如何保证这些传递依赖不会引起版本冲突?
使⽤<dependency>中的<exclusion>元素将会把引起冲突的元素排除。
7.多模块如何聚合
配置⼀个打包类型为pom的聚合模块,然后在该pom中使⽤<module>元素声明要聚合的模块。
8.对于⼀个多模块项目,如果管理项目依赖的版本
通过在⽗模块中声明dependencyManagement和pluginManagement,然后让⼦模块通过<parent>元素指定⽗模块,这样⼦模块在定义依赖时就可以只定义groupId和artifactId,⾃动使⽤⽗模块的version,这样统⼀整个项⽬的依赖的版本。