Java-Spring5(下)
写在前面
- 本文是基于我的上一篇笔记“Spring5笔记(上)”进行的知识点补充,绝大多数知识点已经合并到上一篇里。
手动创建Spring项目(非Maven托管)
- idea中创建普通Java项目(或module)
下载Spring离线jar包,并手动按需导入(导入方法此处省略),其中必须的包为:
- spring-aop
- spring-beans
- spring-context
- spring-core
- spring-expression
commons-logging
- 这个包需要单独下载,而且必须要导入,否则运行时idea会报错找不到包。
- 接下来正常写代码即可。
IOC-工厂bean
- Spring的bean有两种类型:普通bean、工厂bean(FactoryBean)
- 普通bean(前边所讲都是普通bean),在配置文件中定义的bean类型就是返回的类型
工厂bean,在配置文件中定义的bean类型可以和返回类型不一样
- 创建类,让这个类作为工厂bean,实现FactoryBean这个接口
- 实现接口里的方法,在方法
getObject()
中定义返回的bean类型 核心代码
public class MyBean implements FactoryBean<User> { @Autowired private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public User getObject() throws Exception { return this.user; } public Class<?> getObjectType() { return null; } public boolean isSingleton() { return false; } }
IOC-bean的生命周期(主要分5步)
- 通过构造器创建bean实例(无参构造器)
- 给bean中的属性赋值和对其他bean引用(set方法)
调用bean的初始化的方法(需要进行配置初始化的方法)
- 在bean的类中创建myInitMethod()方法
- 在配置文件bean中指定init-method所要调用的方法
- bean可以使用了(对象获取到了)
当容器关闭时调用bean的销毁的方法(需要进行配置销毁的方法)
- 在bean的类中创建myDestroyMethod()方法
- 在配置文件bean中指定init-method所要调用的方法
核心代码
<bean id="myBean" class="cn.cnyasin.pojo.MyBean" init-method="myInitMethod" destroy-method="myDestroyMethod"/>
- 补充:在调用bean初始化方法前后还可以分别添加两步:把bean实例传递给bean的后置处理器的方法,了解一下,此处省略
IOC-配置引入外部属性文件(*.properties),以注册Druid数据库连接池为例
- 导入依赖包:druid-1.2.6.jar、mysql-connector-java-5.1.39-bin.jar
方式一:默认配置文件直接加载
<!-- 注册Druid的bean方式一:默认配置文件直接加载 --> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/jdbc_test?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="rootroot"/> </bean>
方式二:引入外部属性文件
- 添加context约束
在资源目录下新建
jdbc.properties
文件jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/jdbc_test?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=root
配置读取
jdbc.properties
文件<!-- 注册Druid的bean方式二:引入外部属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
使用bean
public static void main(String[] args) throws SQLException { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); DruidDataSource druid = (DruidDataSource) context.getBean("druid"); System.out.println(druid.getConnection()); }
IOC-注解组件扫描配置
常用配置
- use-default-filters:是否使用默认filter,默认true
- context:include-filter:设置扫描哪些内容
- context:exclude-filter:设置不扫描哪些内容
核心代码
<!-- 组件扫描配置一: use-default-filters:是否使用默认filter,默认true context:include-filter:设置扫描哪些内容 --> <context:component-scan base-package="cn.cnyasin.pojo" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 组件扫描配置二: context:exclude-filter:设置不扫描哪些内容 --> <context:component-scan base-package="cn.cnyasin.pojo"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
JdbcTemplate
新建module手动导入包,在原有的基本包的基础上还需要导入以下几个包
- spring-jdbc.jar
- spring-orm.jar
- spring-tx.jar
- 在配置中注册数据库连接池(参考之前的笔记)
- 在配置中开启自动扫描包(参考之前的笔记)
在配置中注册JdbcTemplate对象并注入dataSource
<!-- JdbcTemplate对象 --> <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate"> <!-- 注入dataSource --> <property name="dataSource" ref="druid"/> </bean>
- 数据库创建好表book(略)
创建service、dao、entity并配好注解,以添加一条数据为例:
public interface BookDao { public void add(Book book); } @Repository public class BookDaoImpl implements BookDao{ @Autowired private JdbcTemplate jdbcTemplate; @Override public void add(Book book) { String sql = "insert into book (`name`, `status`,`created_at`) values (?,?,?)"; Object[] args = {book.getName(), book.getStatus(),book.getCreated_at()}; int update = jdbcTemplate.update(sql, args); System.out.println(update); } } @Service public class BookService { @Autowired private BookDao bookDao; public void addBook(Book book){ bookDao.add(book); } } public class Book { private int id; private String name; private int status; private Long created_at; public static final int STATUS_OFF = 0; public static final int STATUS_ON = 1; //getter、setter... } public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); BookService bookService = context.getBean("bookService", BookService.class); // 当前时间戳秒 long time = new Date().getTime() / 1000; bookService.addBook(new Book("红楼梦", Book.STATUS_OFF, time)); } }
- 删除、修改方法与添加一样,这里省略
- 查询某个值的方法:
jdbcTemplate.queryForObject(sql, Integer.class);
查询某个对象的方法:
jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
- 第二个参数:传RowMapper的一个实现类,RowMapper的不同实现类返回不同类型的数据,这里目前使用固定这个类即可
- 查询集合的方法:
jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class), 10);
批量添加的方法:
jdbcTemplate.batchUpdate(sql, list);
核心代码
@Override public void batchAdd(List<Object[]> list) { String sql = "insert into book (`name`, `status`,`created_at`) values (?,?,?)"; int[] ints = jdbcTemplate.batchUpdate(sql, list); System.out.println(Arrays.toString(ints)); } // 批量添加 ArrayList<Object[]> list = new ArrayList<Object[]>(); list.add(new Object[]{"Java", Book.STATUS_ON, time}); list.add(new Object[]{"PHP", Book.STATUS_ON, time}); bookService.batchAdd(list);
- 批量删除、批量修改方法与批量添加一样,这里省略
事务
- 分类:编程试事务、声明试事务
- 编程试事务(传统
try{}catch(){}
方式,不推荐) 声明试事务
基于注解
- 底层采用AOP原理,Spring提供事务API
- Maven添加好需要的依赖(具体参考我的“Maven常用依赖”一文)
配置开启事务
- 开启注解扫描
- 读取jdbc配置文件
- 注册druid
- 注册JdbcTemplate
- 注册事务管理器
- 开启事务注解
创建dao、service,并添加好相关注解
- @Transactional:开启事务(放在方法上该方法开启事务,放在类上类中所有方法都开启事务)
核心代码
<!-- 开启注解扫描 --> <context:annotation-config></context:annotation-config> <context:component-scan base-package="cn.cnyasin"/> <!-- 读取jdbc配置文件 --> <context:property-placeholder location="jdbc.properties"/> <!-- 注册druid --> <bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 注册JdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="druid"/> </bean> <!-- 注册事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druid"/> </bean> <!-- 开启事务注解 --> <tx:annotation-driven transaction-manager="transactionManager"/>
public interface UserDao { // 减余额 void reduceMoney(String username, Long money); // 加余额 void addMoney(String username, Long money); } @Repository public class UserDaoImpl implements UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void reduceMoney(String username, Long money) { String sql = "update user set balance = balance - ? where username = ?"; Object[] args = {money, username}; int update = jdbcTemplate.update(sql, args); } public void addMoney(String username, Long money) { String sql = "update user set balance = balance + ? where username = ?"; Object[] args = {money, username}; int update = jdbcTemplate.update(sql, args); } } @Service public class UserService { @Autowired private UserDaoImpl userDao; @Transactional // 开启事务(放在方法上该方法开启事务,放在类上类中所有方法都开启事务) public void gaveMoney() { String reduceUsername = "Jack"; String addUsername = "Tom"; Long money = 100L; // 减钱 userDao.reduceMoney(reduceUsername, money); int i = 10 / 0; // 加钱 userDao.addMoney(addUsername, money); } }
基于xml
配置开启事务
- 开启注解扫描
- 读取jdbc配置文件
- 注册druid
- 注册JdbcTemplate
- 注册事务管理器
- 配置通知
- 配置切入点和切面
- 删除Java代码层面的
@Transactional
注解
- 其余操作参考上一部分基于注解
核心代码
<!-- 注册事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druid"/> </bean> <!-- 开启事务注解 --> <!--<tx:annotation-driven transaction-manager="transactionManager"/>--> <!-- 通过xml配置文件开启事务 --> <!-- 配置通知 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 配置事务参数 --> <tx:attributes> <!-- 指定那种规则的方法加事务:支持模糊匹配 --> <tx:method name="gaveMoney0" propagation="REQUIRED"/> <!--<tx:method name="gave*"/>--> </tx:attributes> </tx:advice> <!-- 配置切入点和切面 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut id="pt" expression="execution(* cn.cnyasin.service.UserService.*(..))"/> <!-- 配置切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config>
基于完全注解
- 创建配置类
- 配置组件扫描
- 开启事务
- 创建bean方法返回数据库连接池
- 创建bean方法返回JdbcTemplate
- 创建bean方法返回事务管理器
核心代码
@Configuration // 配置类 @ComponentScan("cn.cnyasin") // 组件扫描 @EnableTransactionManagement // 开启事务 public class TxConfig { // 创建数据库连接池 @Bean public DruidDataSource getDruidDataSource() throws IOException { // 读取配置文件 InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(properties.getProperty("jdbc.driverClassName")); druidDataSource.setUrl(properties.getProperty("jdbc.url")); druidDataSource.setUsername(properties.getProperty("jdbc.username")); druidDataSource.setPassword(properties.getProperty("jdbc.password")); return druidDataSource; } // 创建JdbcTemplate @Bean public JdbcTemplate jdbcTemplate(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } // 创建事务管理器 @Bean public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }
声明试事务的参数
propagation:传播行为(多事务进行调用时怎么管理),默认值:
Propagation.REQUIRED
- REQUIRED:当前方法有事务就在该事务中运行,没有事务则创建新事务后在新事务中运行(默认值,常用)
- REQUIRES_NEW:创建新事务,如果原来有事务就挂起原来的,然后在新事务中运行(常用)
- SUPPORTS:有事务就在该事务中运行,没有事务就非事务运行
- NOT_SUPPORTED:非事务运行,如果有事务就挂起该事务后非事务运行
- MANDATORY:有事务就在该事务中运行,没有事务就抛出异常
- NEVER:非事务运行,如果有事务就抛出异常
- NESTED:嵌套事务(注意:这个需要各大厂商的支持)
- isolation:隔离级别(之前讲过了),默认值:
Isolation.DEFAULT
- timeout:超时时间,默认值:-1
readOnly:是否只读,默认值:false
- 如果设置为true,则只能进行查询操作,无法执行增删改操作
rollbackFor:回滚,默认空对象,接受各种异常的class
- 设置出现哪些异常时进行事务回滚
noRollbackFor:不回滚,默认空对象,接受各种异常的class
- 设置出现哪些异常时不进行事务回滚
核心代码
@Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = -1, readOnly = false, rollbackFor = {}, noRollbackFor = {} )