Spring
在不同的场景中表示不同的含义。早期的时候它被用来指代Spring Framework项目本身,这也是它一开始的含义。随着时间的推移,在Spring Framework之上建立了其他Spring项目,比如SpringSecurity、SpringBoot、SpringCloud
。大部分情况下,当人们聊到Spring时,他们所说的 Spring 是 Spring 整个家族。
Spring 是一个轻量级的开源框架,是为解决企业应用开发的复杂性而创建的。而在我的理解中,Spring 的主要就解决了两件事情(当然它还解决了数据访问、远程调用、单元测试等问题),分别对应 Spring 的两个设计思想 IOC 和 AOP:
这里我们就可以解释BeanFactory和ApplicationContext的区别了:
public interface BeanFactory {
// 根据bean名字查找
Object getBean(String name) throws BeansException;
// Bean延迟查找
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
// 判断容器中有没有这个名字的bean
boolean containsBean(String name);
// 判断一个Bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
...
}
BeanFactory 看下去可以去做IOC当中的大部分事情,为什么还要去定义一个ApplicationContext 呢?
我们可以看一下ApplicationContext 的 结构图
从图中可以看到 ApplicationContext 它由BeanFactory接口派生而来,因而提供了BeanFactory所有的功能。除此之外context包还提供了以下的功能:
IoC 全称为 Inversion of Control,翻译为 “控制反转”,说白了,IOC 就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系。
所谓控制:就是对象的创建、初始化、销毁。
所谓反转:
其实是反转的控制权,前面提到是由 Spring 来控制对象的生命周期,那么对象的控制就完全脱离了我们的控制,控制权交给了 Spring 。这个反转是指:我们由对象的控制者变成了 IOC 的被动控制者。
总结:IOC 解决了繁琐的对象生命周期的操作,解耦了我们的代码。
IOC 容器完美解决了耦合问题,甚至可以让互不相关的对象产生注入关系。
在 IOC 模式下,你只需要设计良好的流程和依赖,定义出需要什么,然后把控制权交给 Spring 即可。
DI和DL:即依赖注入和依赖查找。
我们常说IOC和DI,却忽略了DL,其实后两者都是IOC的具体实现,在Spring中主要运用到的是DI,但并不代表没有DL(依赖查找)。例如我们刚学Spring时写的测试程序。
依赖注入主要分为 手动模式 和 自动模式
property
和 constructor-arg
进行依赖设定,这种显式的设定始终会覆盖自动装配。你不能自动装配简单的属性,例如基本类型,Strings
,和 Classes
(以及这些简单属性的数组)。这种限制是由设计造成的。Map
实例,这就不是什么问题了。然而,对于期望单值的依赖项来说,这种歧义性不能随意的解决。如果没有唯一的bean可用,将抛出一个异常。总结:IOC 是一种设计思想。从 IOC 到 DI 和 DL ,就是从理论到实践。程序把依赖交给容器,容器帮你管理依赖,这就是依赖注入的核心。依赖注入降低了开发的成本,提高了代码复用率、软件的灵活性
@Autowired
自动导入对象到类中,被注入进的类同样要被 Spring 容器管理比如:Service 类注入到 Controller 类中。
@Autowired和@Inject、@Resource,可以与@Qualifier或者@Name配合使用,防止多实例注入时出错,以及值注入@Value。
注解 | 解析 | 用法 |
---|---|---|
@Autowired | 通过AutowiredAnnotationBeanPostProcessor类实现的依赖注入,默认是根据类型进行注入的,因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常。 | 可注释在字段上,在方法上 |
@Inject | 作用与@Autowired一样 | 可注释在字段上,在方法上、构造器上 |
@Resource | 默认按照名称进行装配,名称可以通过name属性进行指定 | 可注释在字段上,在方法上 |
@Qualifier | 限定描述符除了能根据名字进行注入,更能进行更细粒度的控制如何选择候选者,可与@Autowired或者@Inject进行组合使用,进行精确注入 | 可注释字段上,在方法上、参数上以及注解中 |
@Scope
作用域和生命周期@Scope 声明 Spring Bean 的作用域 ,四种常见的 Spring Bean 的作用域:
具有4个作用域,以及生命周期过程处理@PostConstruct、@PreDestroy 。
注解 | 解析 | 用法 |
---|---|---|
@Scope | 具有4个作用域singleton,prototype,session,request,默认为singleton单例模式 | 可注释在Class创建时 |
@PostConstruct | 相当于init-method,使用在方法上,当Bean初始化时执行 | 可注释在方法上 |
@PreDestroy | 相当于destory-method,使用在方法上,当Bean销毁时执行 | 可注释在方法上 |
案例:
@Service //组件注入,注明为service组件
@Scope("prototype")//声明Scope为Prototype
public class UseFunctionService {
@Autowired //默认按type注入
@Qualifier("functionService") //精确注入
FunctionService functionService;
@Resource(name="baseDao")//默认按name注入,可以通过name和type属性进行选择性注入
private BaseDao baseDao;
@Inject
@Qualifier("userServiceImpl") //精确注入
public IUserService userService;
@PostConstruct//执行完构造函数后执行
public postConstruct(){
System.out.println("postConstruct");
}
@PreDestroy//在销毁Bean前执行
public perDestroy(){
System.out.println("perDestroy");
}
@Autowired
public void setUserDao(@Qualifier("userDao") UserDao userDao) {
this.userDao = userDao;
}
public String SayHello(String word){
return functionService.sayHello(word);
}
}
@Component
,@Repository
,@Service
, @Controller
@component,而其余 @Controller、@Service、@Repository都组合了@component注解,主要为便于使用者Class组件进行归类。默认加载IOC容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
注解 | 解析 | 用法 |
---|---|---|
@Component | 组件注解,使用了该注解会基于注释的配置和类路径扫描时,会自动扫描并加载Class到ICO容器中 | 注释在类上 |
@Controller | 应用在MVC层(控制层)DispatcherServlet会自动扫描注解了此注解的类,然后将web请求映射到注解了@RequestMapping的方法上 | 注释在类上 |
@Service | 对应服务层,主要涉及一些复杂的逻辑,应用在service层(业务逻辑层) | 注释在类上 |
@Repository | 对应持久层即 Dao 层,主要用于数据库相关操作,应用在dao层(数据访问层) | 注释在类上 |
@Configuration
一般用来声明配置类,可以使用 @Component
注解替代,不过使用Configuration
注解声明配置类更加语义化。 主要区别在于Configuration
在Spring加载的时候,会被CGLib代理,里面的@Bean不会出现重复实例化
官方有说到 ->
@Bean methods are to be declared within @Configuration classes, ensuring that “full” mode is always used and that cross-method references therefore get redirected to the container’s lifecycle management. This prevents the same @Bean method from accidentally being invoked through a regular Java call, which helps to reduce subtle bugs that can be hard to track down when operating in “lite” mode.
机器翻译:在常见的场景中,@Bean方法将在@Configuration类中声明,以确保始终使用“full”模式,并因此将交叉方法引用重定向到容器的生命周期管理。这可以防止通过常规Java调用意外调用相同的@Bean方法,这有助于减少在“lite”模式下操作时难以跟踪的细微bug。
注:读取配置信息@Value,事务注解@Transactional等,还有很多注解就不一一赘述了
总结: SpringMVC 本质上还是在使用Servlet处理,并在其基础上进行了封装简化了开发流程,提高易用性、并使用程序逻辑结构变得更清晰
@RequestMapping
处理常见的 HTTP 请求类型5 种常见的请求类型:
GET /users
(获取所有用户)POST /users
(创建用户)PUT /users/12
(更新编号为 12 的用户)DELETE /users/12
(删除编号为 12 的用户)注解 | 解析 |
---|---|
@GetMapping | @GetMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.GET) |
@PostMapping | @PostMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.POST) |
@PutMapping | @PutMapping("/users/{userId}") 等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.PUT) |
@DeleteMapping | @DeleteMapping("/users/{userId}") 等价于@RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE) |
@PathVariable
和 @RequestParam
@PathVariable
用于获取路径参数,@RequestParam
用于获取查询参数。
举个简单的例子:
@GetMapping("/users/{userId}/teachers")
public List<Teacher> getKlassRelatedTeachers(
@PathVariable("userId") Long userId,
@RequestParam(value = "type", required = false) String type){
...
}
如果我们请求的 url 是:/users/{123456}/teachers?type=web
那么我们服务获取到的数据就是:userIDd=123456,type=web
。
@RequestBody
用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter
或者自定义的HttpMessageConverter
将请求的 body 中的 json 字符串转换为 java 对象。
@RestController
@ResponseBody
注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据,常见于前后端分离项目。
而@RestController
注解是@Controller和
@ResponseBody
的合集,表示这是个控制器 bean,并且是将函数的返回值直 接填入 HTTP 响应体中,是 REST 风格的控制器。
它定义了很多常用的校验注解 一些常用的字段验证的注解
@NotEmpty
被注释的字符串的不能为 null 也不能为空@NotBlank
被注释的字符串非 null,并且必须包含一个非空白字符@Null
被注释的元素必须为 null@NotNull
被注释的元素必须不为 null@AssertTrue
被注释的元素必须为 true@AssertFalse
被注释的元素必须为 false@Pattern(regex=,flag=)
被注释的元素必须符合指定的正则表达式@Email
被注释的元素必须是 Email 格式。@Min(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值@Max(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值@DecimalMin(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值@DecimalMax(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值@Size(max=, min=)
被注释的元素的大小必须在指定的范围内MyBatis
的前身是 iBatis
,iBatis是 Apache 软件基金会下的一个开源项目。2010 年该项目从Apache 基金会迁出,并改名为MyBatis。同期,iBatis停止维护。 MyBatis是一种半自动化的Java持久层框架,其通过注解或XML 的方式将对象和SQL 关联起来。之所以说它是半自动的,是因为和Hibernate 等一些可自动生成SQL的ORM框架相比,使用MyBatis需要用户自行维护SQL,维护SQL 的工作比较繁琐,但也有好处。比如我们可控制SQL逻辑,可对其进行优化,以提高效率。
<configuration>
<properties/>
<settings/>
<typeAliases/>
<typeHandlers/>
<objectFactory/>
<plugins/>
<environments>
<environment>
<transactionManager/>
<dataSource/>
environment>
environments>
<databaseIdProvider/>
<mappers/>
configuration>
例举几个,不一一解释了
着重说一下resultMap吧,这也是mybtais里最复杂的元素,它的作用是定义映射规则,级联的更新,定制类型转化器。
<resultMap id="" type="">
<constructor>constructor>
<id>id>
<result>result>
<association property="">association>
<collection property="">collection>
resultMap>
constructor:constructor主要是用来配置构造方法,默认情况下mybatis会调用实体类的无参构造方法创建一个实体类,然后再给各个属性赋值,但是有的时候我们可能为实体类生成了有参的构造方法,并且也没有给该实体类生成无参的构造方法,这个时候如果不做特殊配置,resultMap在生成实体类的时候就会报错,因为它没有找到无参构造方法。
association 和 collection:是mybatis支持级联的一部分,我们知道在级联中有一对一、一对多、多对多等关系,association主要是用来解决一对一关系的,collection主要是用来解决一对多关系的
public class SqlSessionUtils {
private SqlSessionUtils(){
}
// 配置文件
public static final String resource = "mybatis-config.xml";
/**
* SqlSession是通过SqlSessionFactory来构造的,相当于维护一个连接池,当我们不停的进行查询的时候, * 由于没有关闭连接,导致与数据库的连接数量达到了一个上限
*/
public static SqlSession getDefaultSqlSession() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory.openSession();
}
/**
* SqlSessionManager通过动态代理技术+线程本地变量,升级了DefaultSqlSession的使用。
* 解决了DefaultSqlSession自动关闭和线程安全问题
* @return
* @throws IOException
*/
public static SqlSession getSqlSessionManage() throws IOException {
InputStream inputStream = Resources.getResourceAsStream(resource);
return SqlSessionManager.newInstance(inputStream);
}
}
@Test
public void testUpdateUserInfo() throws IOException {
SqlSession sqlSession = SqlSessionUtils.getDefaultSqlSession();
UserInfo userInfo = new UserInfo();
userInfo.setId(3);
userInfo.setUserName("测试修改");
userInfo.setPassword("111111");
userInfo.setRegDate(new Timestamp(Instant.now().toEpochMilli()));
// sqlSession.update("com.changda.mapper.UserInfoMapper.updateUserInfo");
UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
System.out.println(mapper.updateUserInfo(userInfo));
sqlSession.commit();
}
@Test
public void testUpdateUserInfo() throws IOException {
SqlSession sqlSession = SqlSessionUtils.getSqlSessionManage();
UserInfo userInfo = new UserInfo();
userInfo.setId(3);
userInfo.setUserName("测试修改");
userInfo.setPassword("111111");
userInfo.setRegDate(new Timestamp(Instant.now().toEpochMilli()));
UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
System.out.println(mapper.updateUserInfo(userInfo));
}
<properties>
<spring.version>5.1.8.RELEASEspring.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>${spring.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.3version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.3version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.21version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.2version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>3.0-alpha-1version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.21version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.21version>
dependency>
dependencies>
例举常见的一些xml配置,这里Spring3.0以后其实开始提倡JavaConfig的方式实现去XML配置,大家也不需要全部都记得,尤其Spring5.x版本之后,可以通过导入tomcat的内嵌jar包形式实现零xml配置启动,毕竟紧跟技术潮流嘛。
springmvc.xml
<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"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.zl.controller" />
<mvc:annotation-driven/>
<aop:aspectj-autoproxy/>
<mvc:default-servlet-handler/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/" />
<property name="suffix" value=".jsp" />
bean>
beans>
beans>
applicationContext-dao.xml
<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.xsd">
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="1"/>
<property name="minIdle" value="1"/>
<property name="maxActive" value="10"/>
<property name="maxWait" value="10000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="false"/>
<property name="filters" value="stat" />
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis/sqlMapConfig.xml" />
<property name="mapperLocations" value="classpath:mapping/*.xml">property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.zl.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">property>
bean>
beans>
application-service.xml
<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.xsd">
<context:component-scan base-package="com.zl.service.impl" />
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="WaitForTasksToCompleteOnShutdown" value="true" />
bean>
<context:component-scan base-package="com.zl.aop" />
beans>
applicationContext-trans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="list*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="validate*" read-only="true"/>
<tx:method name="count*" read-only="true"/>
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zl.service.impl.*.*(..))" />
aop:config>
beans>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>FruitSalesdisplay-name>
<welcome-file-list>
<welcome-file>login.jspwelcome-file>
welcome-file-list>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring/applicationContext*.xmlparam-value>
context-param>
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring/springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
能看到helloWord就算搭建完成了
@Controller
public class UserInfoController {
@RequestMapping("/login")
public String login(){
System.out.println("helloword!");
return "index";
}
}