02、Spring
一、引言
1.1、原生web开发中存在哪些问题?
传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。
部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤)。
侵入性强,移植性差(例如:DAO实现的更换,从Connection到sqlSession)。
二、Spring框架
2.1、概念
Spring是一个项目管理框架,同时也是一套Java EE解决方案。
Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。
2.2、访问与下载
官方网站:https://spring.io/
下载地址:http://repo.spring.io/release/org/springframework/spring/
三、Spring架构组成
Spring架构由诸多模块组成,可分类为
核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP。
测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
数据访问:事务,DAO支持,JDBC,ORM,封送XML。
Spring MVC和 Spring WebFlux Web框架。
集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
语言:Kotlin,Groovy,动态语言。
四、自定义工厂
4.1、配置文件
userDao = com.huiruan.dao.impl.UserDaoImpl
userService = com.huiruan.service.impl.UserServiceImpl
4.2、工厂类
public class MyFactory { private Properties properties = new Properties(); public MyFactory() { } public MyFactory(String conf) throws IOException { // 加载配置文件 properties.load(MyFactory.class.getResourceAsStream(conf)); } // 获取对象 public Object getBean(String beanName) throws ClassNotFoundException, IllegalAccessException, InstantiationException { // 获取类路径 String property = properties.getProperty(beanName); if (property != null){ Class claz = null; // 反射:加载类对象 claz = Class.forName(property); // 反射:获取对象 return claz.newInstance(); } return null; } }
五、构建Maven项目
5.1、新建项目

5.2、选择Maven目录

5.3、GAV坐标

六、Spring环境搭建
6.1、pom.xml中引入Spring常用依赖
<dependencies> <!-- spring常用依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> </dependencies>
6.2、创建Spring配置文件
命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.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"> </beans>
七、Spring工厂编码
定义目标Bean类型
public class UserDaoImpl { public void selectUserById(){ System.out.println("Hello World"); } }
spring-context.xml中的<beans >内部配置bean标签
<!-- 配置实例(id:"唯一标识" class="需要被创建的目标对象全限定名") --> <bean id="userDaoImpl" class="com.huiruan.dao.UserDaoImpl"></bean>
调用Spring工厂API (ApplicationContext接口)
public class TestFactory { // 程序中的对象都交由Spring的ApplicationContext工厂进行创建。 public static void main(String[] args) { // 1,读取配置文件中所需创建的bean对象,并获得工厂对象 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); // 2,通过id获取bean对象 UserDaoImpl userDao = (UserDaoImpl) applicationContext.getBean("userDaoImpl"); // 3,使用对象 userDao.selectUserById(); } }
八、依赖与配置文件详解
Spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖Jar包实现功能。
8.1、Spring依赖关系
注意:Jar包彼此存在依赖,只需引入最外层Jar即可由Maven自动将相关依赖Jar引入到项目中。
8.2、schema
配置文件中的顶级标签中包含了语义化标签的相关信息
xmlns:语义化标签所在的命名空间。
xmlns:xsi: XMLSchema-instance标签遵循Schema标签标准。
xsi:schemaLocation: xsd文件位置,用以描述标签语义、属性、取值范围等。
九、loC (Inversion of Control)控制反转【重点】
lnverse Of Controll: 控制反转
反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健
9.1、项目中强耦合问题
public class UserDAOImpl implements UserDA0{...}
public class UserServiceImpl implements UserService { // !!!强耦合了UserDAOImpl!!!,使得UserServicelmpl变得不稳健!! private UserDA0 userDAO = new UserDAOImp1(); @override public User queryUser(){ return userDAO.queryUser(); } }
9.2、解决方案
//不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get) public class UserServiceImpl implements UserService { //!!!不再耦合任何DAO实现!!!,消除不稳健因素!!! private UserDA0 userDA0; //为userDA0定义set/get,允许userDA0属性接收spring赋值 //Getters And Setters @override public User queryUser(){ return userDAO.queryUser(); } }
<bean id="userDao" class="com.huiruan.dao.impl.UserDaoImpl"></bean> <!-- UserServiceImpl组件 --> <bean id="userservice" class="com.huiruan.service.impl.UserServiceImpl"> <!-- 由spring为userDAO属性赋值,值为id="userDao"的bean --> <property name="userDao" ref="userDao"/> </bean>
此时,如果需要更换其他UserDAO实现类,则UserServicelmpl不用任何改动!
则此时的UserServicelmpl组件变得更加稳健!
十、DI (Dependency Injection)依赖注入【重点】
10.1、概念
在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
10.2、Set注入
创建对象时,Spring工厂会通过Set方法为对象的属性赋值。
10.2.1、定义目标Bean类型
public class User { private Integer id; private String password; private String sex; private Integer age; private Date bornDate; private String[] hobbys; private Set<String> phones; private List<String> names; private Map<String,String> countries; private Properties files; }
10.2.2、基本类型+字符串类型+日期类型
<bean name="user" class="com.huiruan.entity.User"> <property name="id" value="1001"></property> <property name="password" value="123456"/> <property name="sex" value="male"/> <property name="age" value="20"/> <property name="bornDate" value="2021/08/24"/> <!-- 注意格式 --> </bean>
10.2.3、容器类型
<bean id="user2" class="com.huiruan.entity.User"> <!--array--> <property name="hobbys"> <array> <value>run</value> <value>swim</value> <value>climb</value> </array> </property> <!-- set --> <property name="phones"> <set> <value>110</value> <value>120</value> <value>119</value> </set> </property> <!--list--> <property name="names"> <list> <value>tom</value> <value>jack</value> <value>marry</value> </list> </property> <!-- map --> <property name="countries" > <map> <entry key="cn" value="china"></entry> <entry key="us" value="america"/> <entry key="kr" value="korea"/> </map> </property> <!-- properties --> <property name="files"> <props> <prop key="first">one</prop> <prop key="second">two</prop> <prop key="third">three</prop> </props> </property> </bean>
10.2.4、自建类型
<!-- 次要bean,被作为属性 --> <bean id="empDao" class="com.huiruan.dao.impl.EmpDaoImpl"></bean> <!-- 主要bean,操作的主体 --> <bean id="empServiceImpl" class="com.huiruan.service.impl.EmpServiceImpl"> <!-- empDao属性引用empDao对象 --> <property name="empDao" ref="empDao"></property> </bean> <!-- 次要bean,被作为属性 --> <bean id="address" class="com.huiruan.entity.Address"> <property name="position" value="广州市番禺区"/> <property name="zipcode" value="100001"/> </bean> <!-- 主要bean,操作的主体 --> <bean id="user3" class="com.huiruan.entity.User"> <!-- User属性引用Address对象 --> <property name="address" ref="address"/> </bean>
10.3、构造注入【了解】
创建对象时,Spring工厂会通过构造方法为对象的属性赋值。
10.3.1、定义目标Bean类型
public class Student { private Integer id; private String name; private String sex; private Integer age; public Student(Integer id, String name, String sex, Integer age) { this.id = id; this.name = name; this.sex = sex; this.age = age; } }
10.3.2、注入
<!-- 构造注入 --> <bean id="student" class="com.huiruan.entity.Student"> <!-- 除标签名称有变化,其他均和Set注入一致 --> <constructor-arg name="id" value="1234"/> <constructor-arg name="name" value="tom"/> <constructor-arg name="age" value="20"/> <constructor-arg name="sex" value="male"/> </bean>
10.4、自动注入【了解】
不用在配置中指定为哪个属性赋值,及赋什么值
由spring自动根据某个"原则",在工厂中查找一个bean,为属性注入属性值
public class EmpServiceImpl implements EmpService { private EmpDao empDao;
// getters and setters ... }
<bean id="empDao" class="com.huiruan.dao.impl.EmpDaoImpl"></bean> <!-- 为EmpServiceImpl中的属性基于类型自动注入值 --> <bean id="empServiceImpl" class="com.huiruan.service.impl.EmpServiceImpl" autowire="byType"></bean>
<bean id="empDao" class="com.huiruan.dao.impl.EmpDaoImpl"></bean> <!-- 为EmpServiceImpl中的属性基于名称自动注入值 --> <bean id="empServiceImpl" class="com.huiruan.service.impl.EmpServiceImpl" autowire="byName"></bean>
十一、Bean细节
11.1、控制简单对象的单例、多例模式
配置<bean scope="singleton | prototype" />
<!-- singleton(默认):每次调用工厂,得到的都是同一个对象。 prototype: 每次调用工厂,都会创建新的对象。 --> <bean id="student" class="com.huiruan.entity.Student" scope="singleton"></bean>
注意: 需要根据场景决定对象的单例、多例模式。
可以共用: Service、DAO、SqlSessionFactory (或者是所有的工厂)。
不可共用: Connection、SqlSession、ShoppingCart。
11.2、FactoryBean创建复杂对象【了解】
作用: 让Spring可以创建复杂对象、或者无法直接通过反射创建的对象。

11.2.1、实现FactoryBean接口

public class MyConnectionFactoryBean implements FactoryBean<Connection> { public Connection getObject() throws Exception { Class.forName("com.mysql.jdbc.Driver"); String url ="jdbc:mysql://localhost:3306/mydb2?userUnicade=true&characterEncoding=utf8"; String passworld ="root"; String username = "root"; Connection connection = DriverManager.getConnection(url,passworld,username); return connection; } public Class<?> getObjectType() { return Connection.class; } public boolean isSingleton() { return false; } }
注意: isSingleton方法的返回值,需根据所创建对象的特点决定返回true/false。
例如: Connection不应该被多个用户共享,返回false。
例如: SqlSessionFactory重量级资源,不该过多创建,返回true。
11.2.2、配置spring-context.xml

<bean id="connection" class="com.huiruan.utils.MyConnectionFactoryBean"/>
11.2.3、特例

十二、Spring工厂特性
12.1、饿汉式创建优势
工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。
提高程序运行效率。避免多次lO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)
12.2、生命周期方法
自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。
自定义销毁方法:添加“destroy-method"属性,Spring则会在销毁对象之前,调用此方法。
销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象。
分类:Singleton对象由Spring容器销毁、Prototype对象由JVM销毁。
public class Address { private String position; private String zipcode; public Address() { System.out.println("构造方法 执行"); } public String getPosition() { return position; } public void setPosition(String position) { System.out.println("set方法执行"); this.position = position; } public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } // 初期化 @PostConstruct public void init(){ System.out.println("init 初期化"); } // 销毁 @PreDestroy public void destroy(){ System.out.println("destroy 销毁"); } }
<bean id="address" class="com.huiruan.entity.Address" init-method="init" destroy-method="destroy"> <property name="position" value="广州市番禺区"/> <property name="zipcode" value="100001"/> </bean>
@Test public void getAddress(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); System.out.println("构建完成"); applicationContext.close(); }
12.3、生命周期阶段
单例bean: singleton
随工厂启动 创建 ==》构造方法·==》set方法(注入值) ==》init(初始化) ==》构建完成==》随工厂关闭销毁
多例bean: prototype
被使用时 创建 ==》构造方法 ==》set方法(注入值)=》init(初始化) ==》构建完成==》JVM垃圾回收销毁
十三、代理设计模式
13.1、概念
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用。

13.2、静态代理设计模式
通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、利于维护。

代理类 = 实现原始类相同接口 + 添加辅助功能 + 调用原始类的业务方法
静态代理的问题
代理类数量过多,不利于项目的管理。
多个代理类的辅助功能代码冗余,修改时,维护性差。
1、FangDongService
public interface FangDongService { public void zufang(); }
2、FangDongServiceImpl
public class FangDongServiceImpl implements FangDongService { public void zufang() { System.out.println("签合同"); System.out.println("收房租"); } }
3、FangDongProxy
public class FangDongProxy implements FangDongService { FangDongServiceImpl fangDongService = new FangDongServiceImpl(); public void zufang() { System.out.println("发布租房信息"); System.out.println("带租户看房"); fangDongService.zufang(); } }
13.3、动态代理设计模式
动态创建代理类的对象,为原始类的对象添加辅助功能。
13.3.1、JDK动态代理实现(基于接口)
public void jdkProxy(){ // 目标 final FangDongService fangDongService = new FangDongServiceImpl(); // 额外功能 InvocationHandler invocationHandler = new InvocationHandler(){ // 设置回调函数(额外功能代码) public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("发布租房信息"); System.out.println("带租户看房"); method.invoke(fangDongService,args); return null; } }; // 创建动态代理类 FangDongService proxy = (FangDongService) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), fangDongService.getClass().getInterfaces(), invocationHandler); proxy.zufang(); }
13.3.2 、CGlib动态代理实现(基于继承)
public void CGProxy(){ final FangDongService fangDongService = new FangDongServiceImpl(); // org.springframework.cglib.proxy.Enhancer // 1、创建字节码增强对象 Enhancer enhancer = new Enhancer(); // 2、设置父类(等价于实现原始类接口) enhancer.setSuperclass(fangDongService.getClass()); //3、设置回调函数(额外功能代码) enhancer.setCallback(new org.springframework.cglib.proxy.InvocationHandler() { public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println("发布租房信息"); System.out.println("带租户看房"); Object invoke = method.invoke(fangDongService, objects); return invoke; } }); // 4、创建动态代理类 FangDongService fangDong = (FangDongService)enhancer.create(); fangDong.zufang(); }
十四、面向切面编程【重点】
14.1、概念
AOP (Aspect Oriented Programming),即面向切面编程,利用一种称为"横切"的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
14.2、AOP开发术语
连接点(Joinpoint):连接点是程序类中客观存在的方法,可被Spring拦截并切入内容。
切入点(Pointcut):被Spring切入连接点。
通知、增强(Advice):可以为切入点添加额外功能,分为︰前置通知、后置通知、异常通知、环绕通知等。
目标对象(Target):代理的目标对象
引介(Introduction):一种特殊的增强,可在运行期为类动态添加Field和Method。
织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
代理(Proxy):被AOP织入通知后,产生的结果类。
切面(Aspect):由切点和通知组成,将横切逻辑织入切面所指定的连接点中。
14.3、作用
Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。
14.4、环境搭建
引入AOP相关依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.6.RELEASE</version> </dependency>
spring-context.xml引入AOP命名空间
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
14.5、开发流程
1、定义原始类
public interface UserService { public void save(); }
public class UserServiceImpl implements UserService{ public void save() { System.out.println("UserServiceImpl 执行啦啦"); } }
2、定义通知类(添加额外功能)
public class MyAdvice implements MethodBeforeAdvice { // 实现前置通知接口 public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("MethodBeforeAdvice 执行啦 "); } }
3、定义bean标签
<!-- 原始对象 --> <bean id="userServiceImpl" class="com.huiruan.service.impl.UserServiceImpl"/> <!-- 辅助对象 --> <bean id="myAdvice" class="com.huiruan.aop.MyAdvice"/>
4、定义切入点(PointCut)
5、形成切面(Aspect)
<aop:config> <!-- 切点 --> <aop:pointcut id="myPointcut" expression="execution(* save())"/> </aop:config> <aop:config> <!-- 组装切面 --> <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointcut"/> </aop:config>
14.6、AOP小结
通过AOP提供的编码流程,更便利的定制切面,更方便的定制了动态代理。
进而彻底解决了辅助功能冗余的问题;
业务类中职责单一性得到更好保障;
辅助功能也有很好的复用性。
14.7、通知类【可选】
定义通知类,达到通知效果
前置通知: MethodBeforeAdvice
后置通知: AfterAdvice
后置通知: AfterReturningAdvice //有异常不执行,方法会因异常而结束,无返回值
异常通知: ThrowsAdvice
环绕通知: MethodInterceptor
1、前置通知
public class MyAdvice implements MethodBeforeAdvice { // 实现前置通知接口 public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println("MethodBeforeAdvice 执行啦 "); } }
2、后置通知
public class UserAfter implements AfterReturningAdvice { //实现后置通知接口 public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("AfterReturningAdvice 执行啦"); } }
3、异常通知
public class UserThrows implements ThrowsAdvice { // 实现异常通知 public void afterThrowing(Exception ex){ System.out.println("ThrowsAdvice 异常通知"); } }
4、环绕通知
public class UserMethod implements MethodInterceptor { // 环绕通知 public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("MethodInterceptor 前执行"); // 触发,执行核心功能 Object inv = invocation.proceed(); System.out.println("MethodInterceptor 后执行"); return inv; } }
14.8、通配切入点
根据表达式通配切入点
<!--匹配参数--> <aop:pointcut id="myPointCut" expression= " execution(* *(com.huiruan.entity.User))"/> <!--匹配方法名(无参)--> <aop:pointcut id="myPointcut" expression="execution(* save())"/> <!--匹配方法名(任意参数)--> <aop:pointcut id="myPointcut" expression="execution(* save(..))" /> <!--匹配返回值类型--> <aop:pointcut id="myPointcut" expression="execution(com.huiruan.entity.User *(..))"/> <!--匹配类名--> <aop:pointcut id="myPointCut" expression="execution(* com.huiruan.service.impl.UserServiceImpl.*(..))"/> <!--匹配包名--> <aop:pointcut id="myPointCut" expression="execution(* com.huiruan.service.impl.*.*(..))"/> <!--匹配包名、以及子包名--> <aop:pointcut id="myPointCut" expression=" execution(* com.huiruan.service..*.*(..))"/>
14.9、JDK和CGLIB选择
spring底层,包含了jdk代理和cglib代理两种动态代理生成机制
基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理
class DefaultAopProxyFactory { //该方法中明确定义了JDK代理和CGLib代理的选取规则 //基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLib代理 public AopProxy createAopProxy(){...} }
14.10、后处理器
spring中定义了很多后处理器;
每个bean在创建完成之前,都会有一个后处理过程,即再加工,对bean做出相关改变和调整;
spring-AOP中,就有一个专门的后处理器,负责通过原始业务组件(Service),再加工得到一个代理组件。

14.10.1、后处理器定义
/** * 定义bean后处理器 * 作用:在bean的创建之后,进行再加工 */ public class MyBeanProcessor implements BeanPostProcessor { /** * 在bean的init方法之前执行 * @param bean 原始的bean对象 * @param beanName * @return * @throws BeansException */ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在bean的init方法之前执行"+bean.getClass()); return bean; } /** * 在bean的init方法之后执行 * @param bean postProcessBeforeInitialization 放回的bean * @param beanName * @return * @throws BeansException */ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在bean的init方法之后执行"+bean.getClass()); // 此处的放回是getBean()最终的返回值 return bean; } }
14.10.2、配置后处理器
<!-- 配置后处理器,将对工厂中所有的bean声明周期进行干预 --> <bean id="myBeanProcessor" class="com.huiruan.processor.MyBeanProcessor"/>
14.10.3、bean生命周期
构造 》注入属性满足依赖 》后处理器前置过程 》初始化 》后处理器后置过程 》返回 》销毁
十五、Spring + MyBatis【重点】
15.1、配置数据源
将数据源配置到项目中
15.1.1、pom.xml
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!-- spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!-- spring整合mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> <properties> <!-- 设置默认编码 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>*.xml</include> <!-- 新添加*/代表1级目录**/代表多级目录 --> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
15.1.2、引入jdbc.properties配置文件
#jdbc.properties jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/huiruan?useUnicode=true&characterEncoding=utf-8 jdbc.username=root jdbc.password=root jdbc.init=1 jdbc.minIdle=1 jdbc.maxActive=3
15.1.3、整合Spring配置文件
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置文件参数化 (参数占位符) --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 与PooledDataSource集成(二选一) --> <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 与DruidDataSource集成(二选一) --> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="${jdbc.init}"/> <property name="minIdle" value="${jdbc.minIdle}"/> <property name="maxActive" value="${jdbc.maxActive}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000"/> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000"/> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000"/> </bean> <!-- 工厂bean:生成SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入连接池 --> <property name="dataSource" ref="druidDataSource"/> <!-- 注入dao-mapper文件信息,如果映射文件和dao接口 同包且同名,则此配置可省略 --> <property name="mapperLocations"> <list> <value>classpath:com/huiruan/dao/*.xml</value> </list> </property> <!-- 为 dao-mapper文件中的实体 定义缺省包路径 如:<select id="queryAll" resultType="User"> 中User类可以不定义包 --> <property name="typeAliasesPackage" value="com.huiruan.entity"/> </bean> </beans>
15.2、整合MyBatis
将SqlSessionFactory、DAO、Service配置到项目中
15.2.1、导入依赖
<!-- spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!-- spring整合mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.1</version> </dependency>
15.2.2、配置sqlSessionFactory
<!-- 工厂bean:生成SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入连接池 --> <property name="dataSource" ref="druidDataSource"/> <!-- 注入dao-mapper文件信息,如果映射文件和dao接口 同包且同名,则此配置可省略 --> <property name="mapperLocations"> <list> <value>classpath:com/huiruan/dao/*.xml</value> </list> </property> <!-- 为 dao-mapper文件中的实体 定义缺省包路径 如:<select id="queryAll" resultType="User"> 中User类可以不定义包 --> <property name="typeAliasesPackage" value="com.huiruan.entity"/> </bean>
15.2.3、UserDao
public interface UserDao { public List<User> selectAll(); }
15.2.4、UserDaoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http ://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace = 所需实现的接口全限定名--> <mapper namespace="com.huiruan.dao.UserDao"> <select id="selectAll" resultType="com.huiruan.entity.User"> select * from t_users </select> </mapper>
15.2.5、测试
@Test public void selectAll(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-content.xml"); SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) applicationContext.getBean("sqlSessionFactory"); SqlSession sqlSession = sqlSessionFactory.openSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> users = mapper.selectAll(); for (User user : users) { System.out.println(user); } }
15.3、配置MapperScannerConfigurer
15.3.1、配置mapperScannerConfigurer
管理DAO实现类的创建,并创建DAO对象,存入工厂管理
1、扫描所有DAO接口,去构建DAO实现
2、将DAO实现存入工厂管理
3、DAO实现对象在工厂中的id是:"首字母小写的-接口的类名"
例如:UserDAO==>userDAO , OrderDAO==>orderDAO
<!-- mapperScannerConfigurer --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- dao接口所在的包 如果有多个包,可以用逗号或分号分隔 <property name="basePackage" value="com.huiruan.dao,com.huiruan1.dao"/> --> <property name="basePackage" value="com.huiruan.dao"/> <!-- 如果工厂中只有一个sq1SessionFactory的bean,此配置可省略 --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
15.3.2、测试
@Test public void mapperScanner(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-content.xml"); UserDao userDao = (UserDao) applicationContext.getBean("userDao"); List<User> users = userDao.selectAll(); for (User user : users) { System.out.println(user); } }
15.4、配置Service
15.4.1、配置Service
<bean id="userService" class="com.huiruan.service.impl.UserServiceImpl"> <!-- 注意ref中的值是对应DA0接口的首字母小写的接口名 --> <property name="userDao" ref="userDao"></property> </bean>
15.4.2、创建UserServiceImpl
public class UserServiceImpl implements UserService { private UserDao userDao; public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } public List<User> selectAll() { return userDao.selectAll(); } }
15.4.3、测试
@Test public void userServiceImpl(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-content.xml"); UserService userService = (UserService) applicationContext.getBean("userService"); List<User> users = userService.selectAll(); for (User user : users) { System.out.println(user); } }
十六、事务【重点】
16.1、配置DataSourceTransactionManager
事务管理器,其中持有DataSource,可以控制事务功能(commit,rollback等)。
<!-- 1.引入一个事务管理器,其中依赖DataSource,借以获得连接,进而控制事务逻辑 --> <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druidDataSource"/> </bean>
注意:DataSourceTransactionManager和SqlSessionFactoryBean要注入同一个DataSource的Bean,否则事务控制失败!!
16.2、配置事务通知
基于事务管理器,进一步定制,生成一个额外功能:Advice。
此Advice可以切入任何需要事务的方法,通过事务管理器为方法控制事务。
<tx:advice id="txManager" transaction-manager="tx"> <tx:attributes> <!--<tx:method name="insertUser" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>--> <!-- 以User结尾的方法,切入此方法时,采用对应事务实行 --> <tx:method name="*User" /> <!-- 以query开头的方法,切入此方法时,采用对应事务实行 --> <tx:method name="select*" propagation="SUPPORTS"/> <!-- 剩余所有方法 --> <tx:method name="*"/> </tx:attributes> </tx:advice>
16.3、事务属性
16.3.1、隔离级别
16.3.1.1、概念
isolation隔离级别

隔离级别由低到高为: read-uncommited < read-commited < repeatable-read < serialized-read
16.3.1.2、特性
安全性: 级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。
并发性: 级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。
16.3.1.3、并发问题
事务并发时的安全问题

16.3.2、传播行为
propagation传播行为
当涉及到事务嵌套(Service调用Service)时,可以设置:
SUPPORTS=不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询)
REQUIRED =不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。(默认值)(适合增删改)
16.3.3、读写性
readonly读写性
true: 只读,可提高查询效率。(适合查询)
false: 可读可写。(默认值)(适合增删改)
16.3.4、事务超时
timeout事务超时时间
当前事务所需操作的数据被其他事务占用,则等待。
100:自定义等待时间100(秒)。
-1:由数据库指定等待时间,默认值。(建议)
16.3.5、事务回滚
rollback-for 回滚属性
如果事务中抛出 RuntimeException,则自动回滚
如果事务中抛出CheckException(非运行时异常Exception),不会自动回滚,而是默认提交事务
处理方案:将CheckException转换成RuntimException上抛,或设置rollback-for="Exception"
public Integer deleteUser(Integer id) { Integer integer = userDao.deleteUser(id); // Integer i = 10/0; if (1==1){ try { throw new SQLException("test 事务"); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("test 事务"); } } return integer; }
<tx:method name="*User" rollback-for="Exception" />
16.4、编织
将事务管理的Advice切入需要事务的业务方法中
<aop:config> <aop:pointcut id="pc" expression="execution(* com.huiruan.service.impl..*.*(..))"/> <aop:advisor advice-ref="txManager" pointcut-ref="pc"/> </aop:config>
十七、注解开发
17.1、声明bean
用于替换自建类型组件的<bean...>标签;可以更快速的声明bean
@Service 业务类专用
@Repository dao实现类专用
@Controller web层专用
@Component 通用
@Scope 用户控制bean的创建模式
//@Service说明 此类是一个业务类,需要将此类纳入工厂 等价替换掉<bean class="xxx.UserServiceImpl"> //@Service默认beanId == 首字母小写的类名"userServiceImpl" //@Service("userService")自定义beanId为"userService" @Service // 声明bean,且id="userServiceImpl" @Scope("singleton") // 声明创建模式,默认为单例模式; @Scope("prototype")即可设置为多例模式 public class UserServiceImpl implements UserService { }
17.2、注入(DI)
用于完成bean中属性值的注入
@Autowired基于类型自动注入
@Resource基于名称自动注入
@Qualifier("userDAO")限定要自动注入的bean的id,一般和@Autowired联用
@Value注入简单类型数据(jdk8种+String)
@Service public class UserServiceImpl implements UserService { @Autowired // 注入类型为UserDAO的bean @Qualifier("userDao") // 如果有多个类型为UserDA0的bean,可以用此注解从中挑选一个 private UserDao userDao; }
@Resource("userDao3") //注入id="userDA03"的bean
private UserDao userDao;
@Resource //注入id="userDao"的bean
private UserDao userDao;
public class User{ @Value("xiaohe") // 注入String private String name; }
17.3、事务控制
用于控制事务切入
@Transactional
工厂配置中的<tx:advice.....和<aop:config….可以省略!!
// 类中的每个方法都切入事务(有自己的事务控制的方法除外) @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED,readOnly = false,rollbackFor = Exception.class,timeout = -1) public class UserServiceImpl implements UserService { // 该方法自己的事务控制,仅对此方法有效 @Transactional(propagation = Propagation.SUPPORTS) public List<User> selectAll() { return userDao.selectAll(); } }
17.4、注解所需配置
<!-- 告知spring,哪些包中有被注解的类、方法、属性 --> <!-- <context:component-scan base-package="com.huiruan.a,com.huiruan.b"/> --> <context:component-scan base-package="com.huiruan"/> <!-- 告知spring,@Transactional在定制事务时,基于 txManager=DataSourceTransactionManager --> <tx:annotation-driven transaction-manager="tx"/>
17.5、AOP开发
17.5.1、注解使用
// 声明此类是一个切面类:会包含切入点(pointcut)和通知( advice) // 声明组件,进入工厂 @Aspect @Component public class MyAspect { // 定义切入点 @Pointcut("execution(* com.huiruan.service.impl.UserServiceImpl.*(..))") public void pc(){} // 前置通知 @Before("pc()") public void myBefore(JoinPoint a){ System.out.println("target"+a.getTarget()); System.out.println("args"+a.getArgs()); System.out.println("name"+a.getSignature().getName()); System.out.println("before==="); } // 后置通知 @AfterReturning(value = "pc()",returning = "ret") public void myAfterReturning(JoinPoint a,Object ret){ System.out.println("after==="+ret); } // 环绕通知 @Around("pc()") public Object myAround(ProceedingJoinPoint p) throws Throwable { System.out.println("Around1"); Object proceed = p.proceed(); System.out.println("Around2"); return proceed; } // 异常通知 @AfterThrowing(value = "pc()",throwing = "ex") public void myAfterThrowing(JoinPoint jp,Exception ex){ System.out.println("AfterThrowing"); System.out.println("==="+ex.getMessage()); } }
17.5.2、配置
<!-- 添加如下配置,启用aop注解 --> <aop:aspectj-autoproxy/>
<!-- 添加如下配置,启用aop注解 --> <!-- but was actually of type 'com.sun.proxy.$Proxy33' 加上 proxy-target-class="true"--> <aop:aspectj-autoproxy proxy-target-class="true" />
十八、集成 JUnit
18.1、导入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.6.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
18.2、编码
可以免去工厂的创建过程;
可以直接将要测试的组件注入到测试类。
// 由SpringJUnit4ClassRunner启动测试 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-content.xml") // spring的配置文件位置 public class SpringRun { // 当前测试类也会被纳入工厂中,所以其中属性可以注入 @Autowired @Qualifier(value = "userServiceImpl") // 注入要测试的组件 private UserServiceImpl userServiceImpl; @Autowired private UserService userService; @Test public void test(){ // 测试使用userDAO List<User> users = userService.selectAll(); for (User user : users) { System.out.println(user); } } // 或者使用这个 @Test public void testUser(){ List<User> users = userServiceImpl.selectAll(); for (User user : users) { System.out.println(user); } } }


浙公网安备 33010602011771号