AOP
我个人理解的AOP,就是有一个目标对象,有通知类,要将通知织入目标对象,在目标对象执行某个方法的前后以及出现异常等等情况做一些事情,也就是加入通知。
通知分为:
1.前置通知:在目标方法执行前执行此通知 2.后置通知(出现异常不会调用):在执行目标方法后面执行 3.环绕通知:出现目标方法前后都调用 4.异常拦截通知:目标方法出现异常会调用此通知方法 5.后置通知(无论有无异常都会调用此方法):执行目标方法后执行
在这里要特别的说一下 aop所用到的jar包:基础包(4+1)+2+2
基础包我之前博客有说过,这个就不说了,只说后面两个+2+2:
- spring-aop-4.2.4.RELEASE.jar
- spring-aspects-4.2.4.RELEASE.jar
- com.springsource.org.aopalliance-1.0.0.jar
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
先给大家看看我通知类的写法(注意里面的环绕通知方法如何写的):
public class MyAdvice {
//前置通知
public void before() {
System.out.println("这是前置通知");
}
//这是后置通知 出现异常不会调用
public void afterReturning() {
System.out.println("这个是后置通知 出现异常不会调用");
}
//环绕通知 出现在目标方法前后都调用
public Object around(ProceedingJoinPoint pJoinPoint) throws Throwable {
System.out.println("这个是调用目标方法前的环绕通知");
Object proceed = pJoinPoint.proceed(); //调用目标方法
System.out.println("这个是调用目标方法后的环绕通知");
return proceed;
}
//异常拦截通知
public void afterException() {
System.out.println("出异常了!");
}
//后置通知 无论出不出现异常 都会调用这个方法
public void after() {
System.out.println("后置通知 无论出不出现异常都会调用这个方法");
}
}
然后说一下配置文件:
<?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-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<!-- 1.配置目标对象 -->
<bean name="userService" class="com.itheima.service.UserServiceImpl"></bean>
<!-- 2.配置通知对象 -->
<bean name="myAdvice" class="com.itheima.aop.MyAdvice"></bean>
<!-- 3.将通知织入目标对象 -->
<aop:config>
<aop:pointcut expression="execution(public void com.itheima.service.UserServiceImpl.addUser())" id="pc"/>
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut-ref="pc"/>
<aop:after method="after" pointcut-ref="pc"/>
<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<aop:around method="around" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
aop 的配置还是分为三步:
- 配置目标对象
- 配置通知对象
- 将通知织入目标对象
注意其中切点和通知的配置
然后是测试类了:
public class test {
@Test
public void fun1(){
//读取配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取目标对象
UserService userService = (UserService) applicationContext.getBean("userService");
//执行方法
userService.addUser();
}
}
测试结果(目标对象方法无异常的情况):
这是前置通知
这个是调用目标方法前的环绕通知
添加用户
这个是调用目标方法后的环绕通知
这个是后置通知 出现异常不会调用
后置通知 无论出不出现异常都会调用这个方法
有异常的运行结果:
这是前置通知
这个是调用目标方法前的环绕通知
添加用户
出异常了!
后置通知 无论出不出现异常都会调用这个方法
又有了新发现,目标方法出现异常后,调用目标方法之后环绕通知不会执行,因为到异常那一步,就不会继续运行了。
这就是我理解的AOP,比较短浅。
Spring整合JDBC
我用的是mysql,首先连接数据库肯定要加jar包:
jar包就这些然后说一下步骤:
- 建表 t_user :两个字段 id 和 name
- 写bean类:user
- 写UserDao接口 :有增删改查查查 这几个方法 后续有代码
- 写UserDao接口的实现类UserDaoImpl:写具体实现
- 写db.properties :也就是数据库的配置信息
- 写applicationContext.xml :也就是写spring的相关配置信息 配置数据库连接池 和配置userDao啊等等
- 写demo测试类: 现在就可以在自己的类里面测试了测试dao中的方法
表结构就不贴了,直接说一下User的代码:
public class User {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
UserDao:
public interface UserDao {
//增
public void addUser(User user);
//刪 根据id删除用户
public void deleteUser(Integer id);
//改 根据id修改用户信息
public void updateUser(User user);
//查 根据id查询相应用户
public User findUserById(Integer id);
//查 查询所有用户的数量
public int getTotalCount();
//查 查询所有用户 返回list集合
public List<User> findAllUer();
}
UserDaoImpl:注意他这里还继承了JdbcDaoSupport 就不用自己配置创建JdbcTemplate模板对象了,可以直接用,这个需要导入spring-tx包 图中有
public class UserDaoImpl extends JdbcDaoSupport implements UserDao {
@Override
public void addUser(User user) {
//先写一个增加的方法再说其他的
String sql = "insert into t_user values(null,?)";
super.getJdbcTemplate().update(sql,user.getName());
}
@Override
public void deleteUser(Integer id) {
String sql = "delete from t_user where id=?";
super.getJdbcTemplate().update(sql,id);
}
@Override
public void updateUser(User user) {
String sql = "update t_user set name=? where id=?";
super.getJdbcTemplate().update(sql,user.getName(),user.getId());
}
@Override
public User findUserById(Integer id) {
String sql = "select * from t_user where id=?";
return super.getJdbcTemplate().queryForObject(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
return user;
}
}, id);
}
@Override
public int getTotalCount() {
String sql = "select count(*) from t_user";
Integer count = super.getJdbcTemplate().queryForObject(sql, Integer.class);
return count;
}
@Override
public List<User> findAllUer() {
String sql = "select * from t_user";
List<User> list = super.getJdbcTemplate().query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
return user;
}
});
return list;
}
}
db.properties:数据库的配置信息,这里的url什么的前缀可以随便加,为的就是和其他的名称不重复
jdbc.driverClass = com.mysql.jdbc.Driver
jdbc.jdbcUrl = jdbc:mysql:///user
jdbc.user = root
jdbc.password = 123456
下面是最重要的Spring的配置信息:注意此处的给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: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-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
<context:property-placeholder location="db.properties"/>
<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.将userdao放入spring容器 -->
<bean name="userDao" class="com.itheima.jdbc.UserDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
然后就到了测试类了,这里用到了Springtest 和Junit4 test整合测试,也就是图中Spring-test包的作用
@RunWith(SpringJUnit4ClassRunner.class) : 指定test测试
@ContextConfiguration(“classpath:applicationContext.xml”) : 指定配置文件,此时候,配置文件在src下,可以不加路径
@Resource(name = “userDao”) : 将配置文件里的userDao的值注入到这个类里面的userDao中来进行操作
package com.itheima.jdbc;
import java.util.List;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.itheima.bean.User;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class demo {
@Resource(name = "userDao")
private UserDao userDao;
// @Test
// public void fun1() throws Exception {
//
// //配置连接池
// ComboPooledDataSource dataSource = new ComboPooledDataSource();
// dataSource.setDriverClass("com.mysql.jdbc.Driver");
// dataSource.setJdbcUrl("jdbc:mysql:///user");
// dataSource.setUser("root");
// dataSource.setPassword("123456");
//
// //自己new对象
// JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// String sql = "insert into t_user values(null,'houge')";
// jdbcTemplate.update(sql);
// }
//测试添加用户
@Test
public void fun2() throws Exception {
User user = new User();
user.setName("张三");
userDao.addUser(user);
}
//测试删除用户
@Test
public void fun3() throws Exception {
userDao.deleteUser(1005);
}
//测试更新用户信息
@Test
public void fun4() throws Exception {
User user = new User();
user.setId(2);
user.setName("chengge");
userDao.updateUser(user);
}
//测试查询用户 根据id查询
@Test
public void fun5() throws Exception {
User user = userDao.findUserById(2);
System.out.println(user);
}
//测试查询所有用户的数量
@Test
public void fun6() throws Exception {
int count = userDao.getTotalCount();
System.out.println(count);
}
//测试查询所有用户
@Test
public void fun7() throws Exception {
List<User> allUer = userDao.findAllUer();
System.out.println(allUer);
}
//测试添加一千个用户
@Test
public void fun8() {
for(int i = 0; i<1000; i++) {
User user = new User();
user.setName("hello world!");
userDao.addUser(user);
}
}
//测试删除一千个用户
@Test
public void fun9() {
long startTime = System.currentTimeMillis();
for (int i = 2290; i > 4; i--) {
userDao.deleteUser(i);
}
long endTime = System.currentTimeMillis();
System.out.println("删除一千个用户成功!全程共花费"+(endTime-startTime)/1000+"秒");
}
}
后面测试加那么多用户只是乱玩的,可以忽略不看。这就是Spring整合jdbc。
事务
事务这个最常用有两种配置方式 有xml配置的,有注解配置的,我先说xml配置的吧
还是先说明jar包:
首先说一下事务的理解:可以看这个大佬的文章:https://blog.csdn.net/weixin_42047611/article/details/80767113
然后就是步骤:
- 先做准备工作:创建表 account 三个字段 id name money money是double类型的
- 创建AccountDao 里面两个方法一个加钱一个减钱的 然后写实现类AccountDaoImpl
- 再写AccountService有一个transfer方法 谁转给谁多少 再写实现类AccountServiceImpl
- 这些准备工作完成之后就是最重要的Spring的配置文件了
- 然后写demo类测试
首先是步骤2:
AccountDao
public interface AccountDao {
//加钱
public void increaseMoney(Integer id, double money);
//减钱
public void decreaseMoney(Integer id, double money);
}
AccountDaoImpl:
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{
@Override
public void increaseMoney(Integer id, double money) {
String sql = "update account set money = money+? where id=?";
getJdbcTemplate().update(sql,money,id);
}
@Override
public void decreaseMoney(Integer id, double money) {
String sql = "update account set money = money-? where id=?";
getJdbcTemplate().update(sql,money,id);
}
}
然后是步骤三:
AccountService:
public interface AccountService {
//转账方法 谁给谁多少钱
public void transfer(Integer from, Integer to, double money);
}
AccountServiceImpl:
public class AccountServiceImpl implements AccountService {
AccountDao aDao;
public void setaDao(AccountDao aDao) {
this.aDao = aDao;
}
@Override
public void transfer(Integer from, Integer to, double money) {
//减钱
aDao.decreaseMoney(from, money);
// int i = 1/0; 这个是用来制造异常来检验事务是否真正的起作用
//加钱
aDao.increaseMoney(to, money);
}
}
步骤四: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:aop="http://www.springframework.org/schema/aop"
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-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<context:property-placeholder location="db.properties"/>
<!-- 事务核心管理器 封装了所有事务操作 依赖于连接池 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" isolation="REPEATABLE_READ" read-only="false" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置织入 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.itheima.service.AccountServiceImpl.transfer(Integer, Integer, double))" id="txPc"/>
<!-- 配置切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPc"/>
</aop:config>
<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.将accountdao放入spring容器 -->
<bean name="accountDao" class="com.itheima.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.accountService -->
<bean name="accountService" class="com.itheima.service.AccountServiceImpl">
<property name="aDao" ref="accountDao"></property>
</bean>
</beans>
然后就是demo:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class demo {
@Resource(name = "accountService")
AccountService accountService;
@Test
public void fun1() {
accountService.transfer(1, 2, 100);
}
}
这样就是xml的配置方法,有一个更为简单的注解配置方法:
配置文件如下:
<tx:annotation-driven/ > 这一行的意思就是告诉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:aop="http://www.springframework.org/schema/aop"
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-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
<context:property-placeholder location="db.properties"/>
<!-- 事务核心管理器 封装了所有事务操作 依赖于连接池 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven/>
<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.将accountdao放入spring容器 -->
<bean name="accountDao" class="com.itheima.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.accountService -->
<bean name="accountService" class="com.itheima.service.AccountServiceImpl">
<property name="aDao" ref="accountDao"></property>
</bean>
</beans>
然后在AccountServiceImpl中的transfer方法上面加上注解,告诉spring是要对这个方法进行事务管理,然后再里面也可以写事务的相关属性
@Transactional(isolation = Isolation.DEFAULT, readOnly = false, propagation = Propagation.REQUIRED)
isolation 是事务的隔离级别
readOnly 是这个方法是否只读数据库 ,因为咱们在这里要对数据库进行修改,所以就是false
Propagation (事务的传播属性)有很多属性,绝大部分用required :表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中,该方法将在那个事务中运行,否则就要开始一个新事务。
代码如下:
public class AccountServiceImpl implements AccountService {
AccountDao aDao;
public void setaDao(AccountDao aDao) {
this.aDao = aDao;
}
@Override
@Transactional(isolation = Isolation.DEFAULT, readOnly = false, propagation = Propagation.REQUIRED)
public void transfer(Integer from, Integer to, double money) {
//减钱
aDao.decreaseMoney(from, money);
// int i = 1/0;
//加钱
aDao.increaseMoney(to, money);
}
}
这就是这几天关于Spring的学习,做一下笔记,磨刀不误砍柴工,不过写博客的过程中也梳理了一下凌乱的代码…