学习Spring5 基础,看这篇就够了
Spring5的内容概述
spring是基于java的又一里程碑产品,几乎是java开发的标配和规范,其中最基础的framework核心是ioc和aop,发展出了web、mvc、security、boot、cloud等全家桶,还有面向主流中间件的data redis、mongodb等产品。
1、Spring概念
(1)、Spring是轻量级的开源的JavaEE框架。
(2)、Spring可以解决企业应用开发的复杂性
(3)、Spring有两个核心部分:
IoC:控制反转,把创建对象过程交给Spring管理(包括创建、初始化、使用、销毁全生命过程)
Aop:面向切面,不修改源代码进行功能增强,如统一日志处理、事务处理、权限控制等;
(4)、Spring特点
- 方便解耦,简化开发
- Aop编程支持
- 方便程序测试
- 方便和其他框架进行整合(但目前情况基本上是用spring全家桶居多,其他第三方辅助为主)
- 方便进行事务操作
- 降低api开发难度
2、IoC容器
控制反转 invert of control,将对象的创建进行反转,常规情况下,对象都是由开发者手动创建,使用ioc开发者不再需要创建对象,而是由ioc容器根据需要自动创建需要的对象,通过spring潜规则+用户配置,简化过程
(1)、ioc底层原理
ioc 控制反转,把创建对象、对象之间的组装调用过程交给spring进行管理。
使用ioc的目的:为了耦合度降低,提高效率,降低编码和维护的工作量和复杂度。
入门案例:a调用b,升级为a调用工程c,间接执行了b的方法。 简单地说,就是第三代理人解耦
ioc实现的三个条件:xml解析、工厂模式、反射
(2)、ioc接口(beanfactory及子接口applicationContext)
beanfactory是ioc容器的基本实现,常用于spring内部,一般不建议项目开发中直接使用。
applicationContext是beanfactory的子类,常用于项目中,与父类的区别在于加载时就创建对象,虽然启动耗时,但使用时较快;另外功能也比父类丰富得多。
(3)、ioc操作bean管理(基于xml)
bean的管理主要指对象的创建和注入两个操作,实现方式有通过xml和注解两种方式。
基于xml的三种注入方式:set、构造函数、p命名空间(注意下方要引入p)
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
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/context/spring-aop-4.3.xsd">
<!-- 同一个vo做了property的set注入和p命名空间的注入,所以用id进行区分,调用时采用“name”的形式,而非xxx.class -->
<!-- bean早期有流行用name而非id的属性,这个主要用于struts1命名斜杠时使用 -->
<bean class="cool.gongju.ioc.Book" id="book1">
<property name="name" value="张三是怎么练成的" />
<property name="pages" value="500页" />
</bean>
<!-- 构造函数还可以用index,按顺序注入 -->
<bean class="cool.gongju.ioc.Book2">
<constructor-arg name="name" value="张三是怎么练成的" />
<constructor-arg name="pages" value="499页" />
</bean>
<bean class="cool.gongju.ioc.Book" id="book2" p:name="张三是怎么练成的" p:pages="488页"></bean>
</beans>
注入空值、特殊符号
<bean class="cool.gongju.ioc.Book" id="book3">
<!-- 像大于号、小于号还可以用html转义字符 <> -->
<property name="name">
<value><![CDATA[<<张三是怎么练成的>>]]]]></value>
</property>
<property name="pages">
<null/>
</property>
</bean>
注入外部bean、内部bean和级联赋值
<!-- 外部bean -->
<bean class="cool.gongju.dao.UserDao" id="userDao"/>
<bean class="cool.gongju.service.UserService" id="userService">
<property name="userDao" ref="userDao"/>
</bean>
<!-- 内部bean -->
<bean class="cool.gongju.service.UserService" id="userService">
<property name="userDao">
<bean class="cool.gongju.dao.UserDao" id="userDao">
<property name="name" value="xxx"/>
</bean>
</property>
</bean>
<!-- 级联赋值写法1 -->
<bean class="cool.gongju.dao.UserDao" id="userDao">
<property name="name" value="xxx"/>
</bean>
<bean class="cool.gongju.service.UserService" id="userService">
<property name="userDao" ref="userDao"/>
</bean>
<!-- 级联赋值写法2-->
<bean class="cool.gongju.dao.UserDao" id="userDao"/>
<bean class="cool.gongju.service.UserService" id="userService">
<property name="userDao" ref="userDao"/>
<property name="userDao.name" value="xxx"/>
</bean>
注入集合类属性:包括数组、list、map、set
<!-- 如果需要注入外部对象,就把value换成ref -->
<bean class="cool.gongju.collectionType.Stu" id="stu">
<property name="courses">
<!--还可以用list-->
<array>
<value>java课程</value>
<value>spring</value>
<value>mqsql</value>
</array>
</property>
<property name="list">
<list>
<value>xxx</value>
<value>yyy</value>
</list>
</property>
<property name="maps">
<map>
<entry key="aa" value="bb"></entry>
<entry key="bb" value="cc"></entry>
</map>
</property>
<property name="sets">
<set>
<value>xxx</value>
<value>yyy</value>
<value>xxx</value>
</set>
</property>
</bean>
注入集合类属性:用util标签
优化
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/context/spring-aop-4.3.xsd">
<util:list id="courses">
<value>java课程</value>
<value>spring</value>
<value>mqsql</value>
</util:list>
<util:list id="list">
<value>xxx</value>
<value>yyy</value>
</util:list>
<util:map id="maps">
<entry key="aa" value="bb"></entry>
<entry key="bb" value="cc"></entry>
</util:map>
<util:set id="sets">
<value>xxx</value>
<value>yyy</value>
<value>xxx</value>
</util:set>
<bean class="cool.gongju.collectionType.Stu" id="stu">
<property name="courses" ref="courses"></property>
<property name="list" ref="list"></property>
<property name="maps" ref="maps"></property>
<property name="sets" ref="sets"></property>
</bean>
</beans>
返回工厂bean:通常情况下,xml中配置的class就是返回的类型,如果要修改,那么实现FactoryBean的getObject方法
package cool.gongju.factoryBean;
import cool.gongju.dao.UserDao;
import org.springframework.beans.factory.FactoryBean;
/**
* @className: Mybean
* @description: 返回工厂bean
* @author: eric 4575252@gmail.com
* @date: 2022/7/21/0021 17:35:31
**/
public class Mybean implements FactoryBean {
public UserDao getObject() throws Exception {
UserDao dao = new UserDao();
return dao;
}
public Class<?> getObjectType() {
return null;
}
}
bean的作用域:设置单实例(默认)或多实例
如果设置scope为prototype,则bean是多实例,如果被反复调用hash地址会变动,而且是每次调用时才新建!
<!-- scope有prototype和singleton -->
<bean id="mybean" class="cool.gongju.factoryBean.Mybean" scope="prototype"></bean>
bean的生命周期:常规五步,构造、填充属性、初始化、正常用、销毁,还有隐藏两步初始化前后,共七步
bean的创建到销毁就是bean的完整生命周期,具体如下:
- 通过构造器创建bean实例(无参数构造)
- 为bean的属性设置值,其他bean引用(调用set)
- 调用bean的初始化方法(需要单独设置)
- bean可以使用了(对象获取到了)
- 当容器关闭时,调用bean的销毁方法(需要单独配置)
<bean id="order" class="cool.gongju.ioc.Order" init-method="init" destroy-method="destory">
<property name="oname" value="老张下单了"/>
</bean>
package cool.gongju.ioc;
public class Order {
private String oname;
public Order() {
System.out.println("第一步,调用了构造函数");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步,对属性值进行填充");
}
public void hello(){
System.out.println("第4步,调用了方法");
}
public void init(){
System.out.println("第3步,调用了初始化");
}
public void destory(){
System.out.println("第5步,调用了销毁");
}
@Test
public void testBeanScope(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
context.getBean("order",Order.class).hello();
context.close();
}
}
bean初始化的后置处理器:不要忘了到xml中进行像普通bean声明!
package cool.gongju.ioc;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @className: MyPost
* @description: TODO 类描述
* @author: eric 4575252@gmail.com
* @date: 2022/7/21/0021 18:05:51
**/
public class MyPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化前被调用了");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化后被调用了");
return bean;
}
}
bean的自动装配
<!-- 通过autowire进行自动装配,有byName和byType两种,如果属性接口有多个实现类,并都声明为bean则只能用byName -->
<bean id="dept" class="cool.gongju.autowire.Dept"/>
<bean id="dept2" class="cool.gongju.autowire.Dept2"/>
<bean id="emp" class="cool.gongju.autowire.Emp" autowire="byName"/>
bean引入外部配置:很常用,如引入德鲁伊的连接池配置
<!-- 注意!要引入context的xsi,然后声明配置文件路径,再用美元符号+花括号进行引用 -->
<bean id="dept" class="cool.gongju.autowire.Dept"/>
<bean id="dept2" class="cool.gongju.autowire.Dept2"/>
<bean id="emp" class="cool.gongju.autowire.Emp" autowire="byName">
<property name="name" value="${prop.test}"/>
</bean>
<context:property-placeholder location="classpath:test.properties"/>
(4)、ioc操作bean管理(基于注解)
注解是特殊代码标记,格式@xxx[(A=B,C=D)]
注解可以作用于类、方法、属性上。
注解的使用目的是简化xml的配置。
基于注解实现对象创建:在xml中配置扫描包配置,类加上专用注解,四选一、任选,建议见名知意 component、controller、service、repository
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
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/context/spring-aop-4.3.xsd">
<context:component-scan base-package="cool.gongju" />
</beans>
package cool.gongju.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
//注解上定义id用value做key,不写默认采用小字母开头的驼峰命名法
}
扫描包细化设置:包含、不包含
<context:component-scan base-package="cool.gongju" >
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
属性注入:
- autowired,根据类型注入
- qualifier,根据名称注入,作用于autowired之上,组合使用!
- resource,同时兼具上面两个,替代上面的组合,这个注解是javax中的,spring不建议使用!
- value,注入普通类型属性
// 默认按class类型搜索匹配
@Autowired
private UserDao userDao;
// 如果有多个实现类,则按名字区分,dao那一头也要提前指定好相应的名称
@Autowired
@Qualifer(name="userdao")
private UserDao userDao;
// 这里不写name按Autowired使用,用了就等同组合效果
@Resource(name="userdao")
private UserDao userDao;
// 对name进行普通属性的注入
@Value(Value="abc")
private String name;
完全注解开发:创建配置类,测试或启动的时候用AnnatationContext来加载!
package cool.gongju;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "cool.gongju")
public class SpringConfig {
}
// Test code
@Test
public void test2(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
System.out.println(context.getBean("userService", UserService.class));
}
3、Aop
面向切面编程,降低耦合度、提高重用性、提高开发效率。不通过修改代码的形式,增加新功能,如添加统一权限控制、日志输出、异常处理等。
底层原理
有两种实现方式,有接口和实现类采用jdk的proxy动态代理,只有父子类的用cglib动态代理
案例:jdk动态代理演示
public interface UserDao {
public int add(int a, int b);
public String update(String id);
}
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
System.out.println("add方法被调用了");
return a+b;
}
@Override
public String update(String id) {
return null;
}
}
public class TestJdk {
@Test
public void testJdkAspect(){
UserDao userDao = new UserDaoImpl();
Class[] interfaces={UserDao.class};
UserDao dao = (UserDao)Proxy.newProxyInstance(TestJdk.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
System.out.println(dao.add(1, 2));
}
class UserDaoProxy implements InvocationHandler{
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置调用");
Object res = method.invoke(obj, args);
System.out.println("后置调用");
return res;
}
}
}
Aop术语:
- 连接点,类中可以被切入的点都是连接。
- 切入点,实际被增强的方法,通过表达式进行过滤
- 通知(增强),实际增强的部分,有5种形式,前置、后置、环绕、异常、最终通知。
- 切面,把通知应用到切入点的过程
Aop操作准备:
- Spring框架基于AspectJ进行切面操作,这个属于独立框架,不是Spring内部
- 基于AspectJ实现AOP操作有两种方式,XML和注解(主流)
- 切入点表达式
execution(权限修饰符 [返回类型] 类全路径.方法(参数列表))
,可用通配符*和.- 其中权限修饰符、类部分路径、方法可用*号做通配符
- 参数列表一般用两个.做模糊匹配
- 返回类型可不写
AspectJ基于XML示例
aspectj的使用需要引入spring context和 aspectj weaver,编写子类和代理类,然后配置xml,xml主要有bean的声明、切入点表达式和增强方法
其中:1、配置部分
<!-- pom依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!-- 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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
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-4.3.xsd">
<bean id="bean" class="cool.gongju.xml.Bean"/>
<bean id="beanProxy" class="cool.gongju.xml.BeanProxy" />
<aop:config>
<aop:pointcut id="p" expression="execution(* cool.gongju.xml.Bean.add(..))"/>
<aop:aspect ref="beanProxy">
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
</beans>
2、java部分
public class Bean {
public int add(int a, int b){
System.out.println("add 方法被调用");
return a+b;
}
}
public class BeanProxy {
public void before(){
System.out.println("BeanProxy before。。。");
}
}
AspectJ基于注解示例
引入pom依赖,再做包扫描和切面自匹配,然后bean都加上componet,切面类加aspect注解就ok啦
其中:1、配置部分
<!-- pom依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<?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"
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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan base-package="cool.gongju.anno"/>
<aop:aspectj-autoproxy/>
</beans>
2、java 部分
@Component
public class User {
public int add(int a, int b){
System.out.println("add 方法被调用");
return a+b; //换成10/0触发异常
}
}
@Component
@Aspect
public class UserProxy {
@Before(value = "execution(* cool.gongju.anno.User.add(..))")
public void before(){
System.out.println("UserProxy before 前置通知。。。");
}
@AfterReturning(value = "execution(* cool.gongju.anno.User.add(..))")
public void afterReturning(){
System.out.println("UserProxy afterReturning 后置通知。。。");
}
@Around(value = "execution(* cool.gongju.anno.User.add(..))")
public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("UserProxy around before 环绕通知。。。");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("UserProxy around after 环绕通知。。。");
return proceed;
}
@AfterThrowing(value = "execution(* cool.gongju.anno.User.add(..))")
public void afterThrowing(){
System.out.println("UserProxy afterThrowing 异常通知。。。");
}
@After(value = "execution(* cool.gongju.anno.User.add(..))")
public void after(){
System.out.println("UserProxy after 最终通知。。。");
}
}
@Test
public void testAspectJAnno(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
User b = context.getBean(User.class);
System.out.println(b.add(1, 2));
}
Aspect 相同切入点抽取去除冗余
用于冗余代码清理,一次编写,方便统一维护
@Pointcut(value = "execution(* cool.gongju.anno.User.add(..))")
public void pointCut(){}
@Before(value = "pointCut()")
public void before(){
System.out.println("UserProxy before 前置通知。。。");
}
@AfterReturning(value = "pointCut()")
public void afterReturning(){
System.out.println("UserProxy afterReturning 后置通知。。。");
}
Aspect 多增强类,对相同方法设置优先级
在多个增强类上加Order注解,范围在0-正无穷,越小优先级越高
@Component
@Aspect
@Order(2)
public class UserProxy2 {
@Pointcut(value = "execution(* cool.gongju.anno.User.add(..))")
public void pointCut(){}
@Before(value = "pointCut()")
public void before(){
System.out.println("UserProxy2 before 前置通知。。。");
}
}
Aspect 全注解
只要增加一个配置类即可
@Configuration
@ComponentScan(basePackages = "cool.gongju.anno")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}
@Test
public void testAspectJAnno2(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
User b = context.getBean(User.class);
System.out.println(b.add(1, 2));
}
4、JdbcTemplate
准备工作
- 引入druid、mysql、jdbc的依赖,数据库表准备
测试案例:
- 对单条行记录进行增加、修改、删除
- 查询单个实体(行)、单个值(如总记录数)、列表(全表)
- 批量操作,这里演示了批量添加,修改或删除也是类似,这里略过
<!-- pom -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.20</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.20</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.2.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.20.164:3306/userdb" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<context:component-scan base-package="cool.gongju.spring5"/>
</beans>
sql
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
java
@ToString
@Data
public class User {
private String userId;
private String userName;
private String status;
}
public interface UserDao {
void addUser(User user);
void updateUser(User user);
void delUser(String id);
void findUserByID(String id);
void findUserCount();
void findAllUser();
void batchAdd();
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void addUser(User user) {
String sql = "insert into t_user values(?,?,?)";
int update = jdbcTemplate.update(sql, user.getUserId(), user.getUserName(), user.getStatus());
System.out.println(update);
}
@Override
public void updateUser(User user) {
String sql = "update t_user set user_name=?,status =? where user_id =?";
int update = jdbcTemplate.update(sql, user.getUserName(), user.getStatus(), user.getUserId());
System.out.println(update);
}
@Override
public void delUser(String id) {
String sql = "delete from t_user where user_id = ?";
int update = jdbcTemplate.update(sql, id);
System.out.println(update);
}
@Override
public void findUserByID(String id) {
String sql = "select * from t_user where user_id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
System.out.println(user);
}
@Override
public void findUserCount() {
String sql = "select count(*) from t_user";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(integer);
}
@Override
public void findAllUser() {
String sql = "select * from t_user";
List<User> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));
System.out.println(list);
}
@Override
public void batchAdd() {
String sql = "insert into t_user values(?,?,?)";
List<Object[]> list = new ArrayList<Object[]>();
list.add(new Object[]{"3", "java", "heihei"});
list.add(new Object[]{"4", "c++", "hoho"});
list.add(new Object[]{"5", "php", "hehe"});
int[] ints = jdbcTemplate.batchUpdate(sql, list);
System.out.println(Arrays.toString(ints));
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void addUser(){
User user = new User();
user.setUserId("1");
user.setUserName("张三");
user.setStatus("ok");
userDao.addUser(user);
}
public void delUser(){
userDao.delUser("1");
}
public void updateUser(){
User user = new User();
user.setUserId("1");
user.setUserName("李四");
user.setStatus("ok");
userDao.updateUser(user);
}
public void findUserByID(){
userDao.findUserByID("1");
}
public void findUserCount(){
userDao.findUserCount();
}
public void findAllUser(){
userDao.findAllUser();
}
public void batchAdd() {
userDao.batchAdd();
}
}
5、事务管理
事务是数据库操作的一个基本单元,一般包含一个或多个任务,执行时要么一起成功,要么一起失败,例如在转账时两人的账号余额加减变化!
事务的四个特性(ACID):
- 原子性,(Atomicity)事务中的多个任务要么成功一起提交,要么失败回滚到事务执行初期的状态。
- 一致性,(Consistency)事务进行改变的前后,总体不变,如两人余额的总数在转账前后都是一致的,不会凭空多一些或少一些。
- 隔离性,(Isolation)多个事务之间是不会互相影响,主要指某个事务会不会读到另一个事务未提交的数据。
- 持久性,(Durability)事务执行后数据会被持久化,如写入磁盘文件或数据库
事务的操作:建议控制在service层,有传统编程式控制、spring xml或注解的声明式控制,依赖的是aop的技术
并发事务的隔离控制,事务并发操作时,隔离性可能存在三种问题:
- 脏堵,事务B读取到事务A修改后但未提交的数据,事务A发生回滚,事务B之前拿到的数据是脏数据。
- 不可重复读,并发修改时,事务AB初期拿到一样的数据,如果两者前后对数据进行修改,导致数据错乱,应串行读取、修改。
- 幻读,两个事务并行处理时,事务A提交新增数据的前后,事务B两次查询的记录总数不一致。
spring事务的默认隔离参数为Repeatable Read
,限制了脏读和不可重复读,幻读需要设置为串行才可彻底解决,不过牺牲了性能,可参考mysql mvcc版本控制技术!
事务中可设置参数:声明式事务管理参数配置
- 传播属性,propagation,参考下图,默认Required
- 事务隔离级别,ioslation,默认Repeatable Read
- 只读,readOnly 默认非只读,可查询、可修改;可设置为只读true,只能查询
- 超时,timeOut 默认-1,不超时,可设置数字1-正无穷,单位秒
- 回滚,rollbackFor,设置回滚触发的异常类class
- 不会滚,noRollbackFor,设置不回滚触发的异常类class
传统java中jdbc的事务控制: 通过try、catch、finally和jdbc的事务手工控制进行操作。
@Test
public void demo2(){
//模拟转账操作,使用事务管理
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = JDBCUtils.getConnection();
//在连接获得后,开启事务
conn.setAutoCommit(false); //关闭自动提交
String sql1 = "update account set money = money - 100 where name = 'aaa'";
String sql2 = "update account set money = money + 100 where name = 'bbb'";
stmt = conn.prepareStatement(sql1);
stmt.executeUpdate();
int d = 1 / 0;
stmt = conn.prepareStatement(sql2);
stmt.executeUpdate();
//如果程序能走到这一步 说明两句sql都执行成功 提交事务
conn.commit();
} catch (Exception e) {
//表示在执行转账的过程产生了异常 需要将两句sql语句进行回滚
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
基于xml+注解的spring事务控制
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.20.164:3306/userdb" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 组合式主要用到这个注解驱动+类上面的注解标准-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<context:component-scan base-package="cool.gongju.spring5"/>
</beans>
java部分
package cool.gongju.spring5.service;
import cool.gongju.spring5.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
//主要用到了 Transactional标记
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
public void moneyChange(){
userDao.addMoney();
// 手工设置异常的生成
int i = 10/0;
userDao.reduceMoney();
}
}
基于xml的spring事务控制
service上的事务注解可以拿掉了
<?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"
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/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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.20.164:3306/userdb" />
<property name="username" value="root" />
<property name="password" value="123456" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 这里不需要注解驱动了 <tx:annotation-driven transaction-manager="transactionManager"/>-->
<!-- 设置在那个方法上设置何种事务-->
<tx:advice id="txadvice">
<tx:attributes>
<tx:method name="moneyChange" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 设置切入点、切面操作方式 -->
<aop:config>
<aop:pointcut id="pt" expression="execution(* cool.gongju.spring5.service.UserService.*(..))"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
<context:component-scan base-package="cool.gongju.spring5"/>
</beans>
基于完全注解的spring事务控制
增加配置类,这里对数据库配置还有硬编码,在springboot中会从配置文件中加载!
package cool.gongju.spring5;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* @className: TxConfig
* @description: TODO 类描述
* @author: eric 4575252@gmail.com
* @date: 2022/7/22/0022 17:01:02
**/
@Configuration
@ComponentScan(basePackages = "cool.gongju.spring5")
@EnableTransactionManagement
public class TxConfig {
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://192.168.20.164:3306/userdb");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
@Test
public void testChangeMoney2(){
ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = context.getBean(UserService.class);
userService.moneyChange();
}
6、Spring5新特性
log4j2
<!-- POM -->
<!-- log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.15.0</version>
</dependency>
<!-- log4j-api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.15.0</version>
</dependency>
<!-- log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.15.0</version>
<scope>test</scope>
</dependency>
<!-- slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!-- log4j2.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration 后面的 status 用于设置 log4j2 自身内部的信息输出,可以不设置,当设置成 trace 时,可以看到 log4j2 内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的 appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义 logger,只有定义 logger 并引入的 appender,appender 才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定 Logger,则会使用 root 作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
java 中使用
package cool.gongju.spring5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Log4JTest {
private static final Logger log = LoggerFactory.getLogger(Log4JTest.class);
public static void main(String[] args) {
log.info("hello,dengzi");
log.warn("hello,dengzi");
}
}
Junit4
<!-- pom -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.20</version>
</dependency>
java, 可以选择用xml或全注解的方式
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TxConfig.class)
//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(locations = {"classpath:bean.xml"})
//@ContextConfiguration("classpath:bean.xml")
public class TestJdbc {
@Autowired
private UserService userService;
@Test
public void testChangeMoney2(){
userService.moneyChange();
}
Junit5
以下配置用于spring framework, 如果是springboot那是另一种写法, 参考 SpringBoot与单元测试JUnit的结合 - 小鱼吃猫 - 博客园 (cnblogs.com)
<!-- pom -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.20</version>
</dependency>
java, 可以选择用xml或全注解的方式, 注意,这里的Test标签是不一样的!
import cool.gongju.spring5.service.UserService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = TxConfig.class)
//@ExtendWith(SpringExtension.class)
//@ContextConfiguration(locations = {"classpath:bean.xml"})
public class TestJdbc {
@Autowired
private UserService userService;
@Test
public void testChangeMoney2(){
userService.moneyChange();
}
}
评论系统未开启,无法评论!