一、初步认识
-
Spring是什么?
Spring是为简化JAVAEE 应用开发而创建的框架。
-
为什么使用Spring?
解决企业应用开发的复杂性。
-
Spring有什么功能?
- 核心技术:依赖注入,事务,资源,国际化,验证,数据绑定,类型一致性。
- 测试:mock对象,测试上下文框架,SpringMVC测试,浏览器测试。
- 数据访问:数据库事务,持久层支持,JDBC,对象关系映射。
- web框架:SpringMVC,Spring WebFlux。
- 集成:远程,事务,任务管理,缓存。
- 语言支持:KOTLIN,GROOVY,Dynamic languages。
-
Spring的历史
一切要从bean说起,1996年sun公司发布了javaBean 1.00-A规范,使得简单的java对象不仅可以被重用,而且还可以轻松地构建更复杂的应用。但javabean作为可重用的应用组件太过简易,无法提供复杂的服务比如事务、安全、分布式计算的支持。
1998年sun公司发布了EJB 1.0规范,把java组件的设计理念延申到服务器端,并提供了许多必需的企业级服务。此时,EJB Bean和JavaBean无任何关系。EJB简化了许多基础架构层面的开发,但另一方面在部署描述符和配套代码等方面变得异常复杂。违背了它当初的理念:简化企业级应用开发。
现在JAVA组件开发理念重新回归正轨。新的编程技术包括AOP和DI,它们为javabean提供了之前EJB才能拥有的强大功能。EJB的发展促进了基于POJO的编程模型,而Spring框架成为基于POJO的轻量级开发框架的领导者。
-
IOC(控制反转)
对象由Spring来创建、统一管理,不再由使用对象显式创建。
6.容器
7.AOP(面向切面编程概念)
通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
切面(aspect) 一种切断多个类的关注点模块,被Spring具象化为一个类 (log类)
通知(advice) 切面必须要完成的一个任务,是切面里的方法 (log里的一个方法)
连接点(join point) 程序执行的一个点 (执行方法的地点)
切入点(pointcut) 匹配连接点的术语 (执行方法的地点)
目标对象(Target Object) 被一个或多个切面通知的对象 (被通知的接口或方法)
代理(AOP proxy) 为实现切面契约而被AOP框架创建的对象
织入(weaving) 连接切面与其它应用对象去创建被通知对象
参考资料:
官网:https://spring.io/projects/spring-framework
官方下载:https://repo.spring.io/release/org/springframework/spring/
Github下载:https://github.com/spring-projects/spring-framework
二、进阶认识
1.容器加载配置文件的类继承结构
2.bean中创建对象的方式
<!--方式一、无参构造器-->
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
<!--方式二、通过静态工厂方法-->
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
<!--方式三、通过已存在的bean的对象工厂方法 -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
3.bean中的依赖注入
3.1基于构造器的依赖注入
<!--注入对象-->
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
<!--通过类型匹配,注入基本类型参数-->
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
<!--通过索引匹配,注入基本类型参数-->
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
<!--通过名称匹配,注入基本类型参数-->
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
3.2基于set方法的依赖注入
首先创建一个示例类
package com.example.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Person {
private String name;
private Address address;
private String[] books;
private List<String> hobby;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
//省略setter方法
}
通过以下方式注入
<!--类必须实现setter方法才可以使用-->
<bean id="address" class="com.example.pojo.Address"/>
<bean id="person" class="com.example.pojo.Person">
<!-- 第一种,普通值注入-->
<property name="name" value="longzequan"/>
<!-- 第二种,bean对象注入-->
<property name="address" ref="address"/>
<!--array-->
<property name="books">
<array>
<value>JAVA</value>
<value>PHP</value>
<value>Python</value>
</array>
</property>
<!--list-->
<property name="hobby">
<list>
<value>听歌</value>
<value>看小说</value>
<value>玩游戏</value>
</list>
</property>
<!--map-->
<property name="card">
<map>
<entry key="身份证" value="4212211234568794621"/>
<entry key="银行卡" value="63216546546146516516146"/>
</map>
</property>
<!--set-->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>SCROLL</value>
</set>
</property>
<property name="wife">
<null/>
</property>
<!--properties-->
<property name="info">
<props>
<prop key="学号">10010</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
3.3拓展方式注入
<!--p命名空间注入注入-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
<!--c命名空间注入注入-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
<!-- traditional declaration with optional argument names -->
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="something@somewhere.com"/>
</bean>
<!-- c-namespace declaration with argument names -->
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
</beans>
4.bean的作用域
<!--默认是单例模式-->
<bean id="blog" class="com.example.pojo.Blog"/>
<!--给定作用域,原型模式-->
<bean id="blog" class="com.example.pojo.Blog" scope="prototype"/>
<!--给定作用域,web开发模式用到-->
<bean id="blog" class="com.example.pojo.Blog" scope="request"/>
<bean id="blog" class="com.example.pojo.Blog" scope="session"/>
<bean id="blog" class="com.example.pojo.Blog" scope="application"/>
5.bean的自动装配
使用@Autowired
<!--在容器上下文中通过类型或名称查找-->
<bean id="person" class="com.example.pojo.Person" autowire="byName"/>
<bean id="person" class="com.example.pojo.Person" autowire="byType"/>
通过注解来自动装配,首先要在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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
private MovieFinder movieFinder;
//set方法上
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
//普通方法上
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
//字段上
@Autowired
private MovieCatalog movieCatalog;
//构造器上
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
//若设置required为false,说明这个属性可以为空
@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
//若有多个构造器,可加上@Primary指定某一个为主构造器
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
//若有多个对象,可以@Qualifier指定bean对象
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
//可以用java自带的注解@Resource
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
使用注解的方式装配对象与XML的方式相比,好处是在声明的地方就能看到,减少了配置,缺点是每次改动代码需要再编译,不便于集中控制。
最佳实践:
所有的bean管理交由xml来完成,而属性注入交由注解来完成。
6.使用注解开发
<!--自动扫描包-->
<context:component-scan base-package="com.example.pojo"/>
<context:annotation-config/>
接下来直接在java类中使用注解
package com.example.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@Component
//@Repository DAO层注解
//@Service Service层注解
//@Controller Controller层注解
//@Scope("prototype") 作用域注解
public class Blog {
private String title;
public Blog(String title) {
this.title = title;
}
public Blog() {
}
public String getTitle() {
return title;
}
@Value("spring")
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "Blog{" +
"title='" + title + '\'' +
'}';
}
}
使用基于java的配置文件
package com.example.config;
import com.example.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//@Configuration代表一个配置类,代表xml配置文件
@Configuration
public class UserConfig {
//@Bean相当于一个bean标签,id等于方法名,class等于返回值类型
@Bean
public User getUser(){
return new User();
}
}
7.使用Spring实现AOP
7.1 通知(advice)的种类
- 前置通知
- 前置通知
- 后置通知
- 异常通知
- 退出通知
- 环绕通知
7.2 引入jar包
使用切面aspectJ需要jar包的支持
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
方式一、使用SPRING API接口
在java中实现两个通知类
package com.example.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class log implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("log:执行了"+method.getName());
}
}
package com.example.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("afterlog:执行了"+method.getName()+",返回值为"+returnValue);
}
}
在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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册对象-->
<bean id="userServiceImpl" class="com.example.Service.UserServiceImpl"/>
<bean id="log" class="com.example.log.log"/>
<bean id="afterLog" class="com.example.log.AfterLog"/>
<!--方式一、使用SPRING API接口-->
<aop:config>
<!--定义切入点,expression表达式execution(要执行的位置)-->
<aop:pointcut id="logPointCut" expression="execution(* com.example.Service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="logPointCut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="logPointCut"/>
</aop:config>
</beans>
方式二、自定义类
在java中自定义类
package com.example.pointcut;
public class customPointcut {
public void beforeInfo(){
System.out.println("====执行前====");
}
public void afterInfo(){
System.out.println("====执行后====");
}
}
在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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--方式二,自定义类-->
<bean id="customPointcut" class="com.example.pointcut.customPointcut"/>
<aop:config>
<!--自定义切面,引用自定义类-->
<aop:aspect ref="customPointcut">
<!--定义切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.example.Service.UserServiceImpl.*(..))"/>
<!--定义通知-->
<aop:before method="beforeInfo" pointcut-ref="pointcut"/>
<aop:after method="afterInfo" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
方式三、使用注解
package com.example.pointcut;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
@Aspect
public class Annotation {
@Pointcut("execution(* com.example.Service.UserServiceImpl.*(..))")
public void test(){
}
@Around("test()")
public Object aroundInfo(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("==方法环绕前==");
Object retVal =joinPoint.proceed();
System.out.println("==方法环绕后==");
return retVal;
}
@Before("test()")
public void beforeInfo(){
System.out.println("==方法执行前==");
}
@After("test()")
public void afterInfo(){
System.out.println("==方法执行后==");
}
}
在xml中开启注解配置
<!--方式三,基于注解-->
<bean id="annotation" class="com.example.pointcut.Annotation"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
8.expression表达式
//匹配所有公共方法
execution(public * *(..))
//所有以set方法开头的方法
execution(* set*(..))
//所有以接口AccountService定义的方法
execution(* com.xyz.service.AccountService.*(..))
//所有service包下的所有方法
execution(* com.xyz.service.*.*(..))
//所有service包或子包的所有方法
execution(* com.xyz.service..*.*(..))
//service包下的所有连接点
within(com.xyz.service.*)
//service包或子包的连接点
within(com.xyz.service..*)
//AccountService接口下的连接点
this(com.xyz.service.AccountService)
三、快速使用
1.导入jar包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
2.创建实体类
package com.example.pojo;
public class Blog {
private String title;
public Blog(String title) {
this.title = title;
}
public Blog() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "Blog{" +
"title='" + title + '\'' +
'}';
}
}
3.创建bean配置文件
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过Spring创建对象的方式,创建bean标签,id代表变量名,class代表类名-->
<bean id="blog" class="com.example.pojo.Blog">
<!--通过property的value来注入属性-->
<property name="title" value="Spring"/>
<!--若是注入对象,则通过ref注入-->
<property name="title" ref="author"/>
</bean>
</beans>
4.在客户端创建对象测试
//配置文件加载时,bean中的对象就已经完成初始化创建了
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Blog blog = (Blog) context.getBean("blog");
System.out.println(blog.toString());
四、Spring整合
1.整合mybatis
参考资料:https://mybatis.org/spring/zh/getting-started.html
1.1导入依赖
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
1.2编写配置文件
<!--使用Spring的数据源替换mybatis的配置 c3p0 druid-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone = GMT"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--通过SqlSessionFactoryBean创建sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="datasource"/>
<!--配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/example/dao/userMapper.xml"/>
</bean>
<!--创建sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<!--注入userMapper-->
<bean id="userMapper" class="com.example.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
1.3编写实现类
package com.example.dao;
import com.example.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getUserList() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUserList();
}
@Override
public User getUserById(int id) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUserById(id);
}
@Override
public Integer addUser(User user) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.addUser(user);
}
@Override
public Integer updateUser(User user) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.updateUser(user);
}
}
1.4测试
public void myTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapperImpl = (UserMapper) context.getBean("userMapper");
userMapperImpl.getUserList();
}
1.5配置声明式事务
编写xml文件
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="datasource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.example.dao.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>