public class Account {private Integer id;private String name;private Double money;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;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
public interface AccountDao {@Insert("insert into tbl_account(name,money)values(#{name},#{money})")void save(Account account);@Delete("delete from tbl_account where id = #{id} ")void delete(Integer id);@Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")void update(Account account);@Select("select * from tbl_account")List findAll();@Select("select * from tbl_account where id = #{id} ")Account findById(Integer id);
}
public class App {public static void main(String[] args) throws IOException {// 1. 创建SqlSessionFactoryBuilder对象SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();// 2. 加载SqlMapConfig.xml配置文件InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 3. 创建SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);// 4. 获取SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();// 5. 执行SqlSession对象执行查询,获取结果UserAccountDao accountDao = sqlSession.getMapper(AccountDao.class);Account ac = accountDao.findById(2);System.out.println(ac);// 6. 释放资源sqlSession.close();}
}
public interface AccountService {void save(Account account);void delete(Integer id);void update(Account account);List findAll();Account findById(Integer id);}
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void save(Account account) {accountDao.save(account);}public void update(Account account){accountDao.update(account);}public void delete(Integer id) {accountDao.delete(id);}public Account findById(Integer id) {return accountDao.findById(id);}public List findAll() {return accountDao.findAll();}
}
1. 分析
sqlSession是工厂造出来的,已经存在,现在调用而已
sqkSessionFactory是核心对象
下面配置是围绕此对象进行的
org.springframework spring-context 5.2.10.RELEASE com.alibaba druid 1.1.16 org.mybatis mybatis 3.5.6 mysql mysql-connector-java 5.1.47 org.springframework spring-jdbc 5.2.10.RELEASE org.mybatis mybatis-spring 1.3.0 org.mybatis mybatis-spring 1.3.0
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {}
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}
}
public class MybatisConfig {//定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itheima.domain");ssfb.setDataSource(dataSource);return ssfb;}//定义bean,返回MapperScannerConfigurer对象@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itheima.dao");return msc;}
}
public class App2 {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);AccountService accountService = ctx.getBean(AccountService.class);Account ac = accountService.findById(1);System.out.println(ac);}
}
junit junit 4.12 org.springframework spring-test 5.2.10.RELEASE
junit就相当于不用走main方法直接执行
//设定类运行器
@RunWith(SpringJUnit4ClassRunner.class)
//配置上下文
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {//测业务层接口//自动装配@Autowiredprivate AccountService accountService;//写测试方法@Testpublic void testFindById(){//直接掉方法就可以啦System.out.println(accountService.findById(2));}}
AOP面向切面编程,一种编程规范,指导开发者如何组织程序结构
在不惊动原始设计的基础上为其进行功能增强
将原来想要其他方法也拥有的功能代码抽取出来,把这块东西抽取出来成method单独的方法
给原来就有的方法取名叫做连接点
给需要追加功能的方法叫做切入点
追加的共性功能的方法(method)中叫做通知
通知方法所在的类叫做通知类
通知和切入点关系叫做切面
导入context的时候,aop的包也自动导入了
导入aspectj的包
org.springframework spring-context 5.2.10.RELEASE org.aspectj aspectjweaver 1.9.4
public interface BookDao {public void save();public void update();
}
@Repository
public class BookDaoImpl implements BookDao {public void save() {System.out.println(System.currentTimeMillis());System.out.println("book dao save ...");}public void update(){System.out.println("book dao update ...");}
}
//4.通知类必须配置成Spring管理的bean
@Component
//5.设置当前类为切面类类,spring就会把这个当作aop处理
@Aspect
public class MyAdvice {//2.定义切入点,要求配置在方法上方@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}//3.设置在切入点pt()的前面运行当前操作(前置通知)@Before("pt()")//1.共性功能public void method(){System.out.println(System.currentTimeMillis());}
}
@Configuration
@ComponentScan("com.itheima")
//开启注解开发AOP功能,跟@Aspect一起的
@EnableAspectJAutoProxy
public class SpringConfig {
}
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);BookDao bookDao = ctx.getBean(BookDao.class);bookDao.update();}
}
1668245211570
book dao save ...
System.out.println(bookDao);打印出来是System.out.println(bookDao);
System.out.println(bookDao.getClass());打印出来是class com.sun.proxy.$Proxy19
System.out.println(bookDao);打印出来是System.out.println(bookDao);
System.out.println(bookDao.getClass());打印出来是class com.itheima.dao.impl.BookDaoImpl
打印对象不准确,因为对tostring进行重写了,所以看到的是一个样的
打印.class发现是由代理完成的实现的
类型
按照接口描述
按照接口实现类描述,但一般不用,耦合度高
切入点表达式标准格式
@Pointcut("execution(void com.itheima.dao.BookDao.update())")@Pointcut("execution(void com.itheima.dao.impl.BookDaoImpl.update())")
@Pointcut("execution(* com.itheima.dao.impl.BookDaoImpl.update())")//出@Pointcut("execution(* com.itheima.dao.impl.BookDaoImpl.update(*))")//不出,因此*表示至少一个
@Pointcut("execution(void *..update())")@Pointcut("execution(* *..*(..))")//表示匹配工程中的所有东西,电脑估计会炸,一般不用@Pointcut("execution(* *..u*(..))")//u开头的方法
描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
public class MyAdvice {@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}//@Before:前置通知,在原始方法运行之前执行@Before("pt()")public void before() {System.out.println("before advice ...");}
}
//@After:后置通知,在原始方法运行之后执行@After("pt()")public void after() {System.out.println("after advice ...");}
需要加上对原始操作的调用
//@Around:环绕通知,在原始方法运行的前后执行@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Object ret = pjp.proceed();System.out.println("around after advice ...");return ret;}
如果接口有返回值,@Around需要返回Object,通过 pjp.proceed()进行返回值的接收
public interface BookDao {public int select();
}
public class BookDaoImpl implements BookDao {public int select() {System.out.println("book dao select is running ...");
// int i = 1/0;return 100;}
}
@Around("pt2()")public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Integer ret = (Integer) pjp.proceed();System.out.println("around after advice ...");return ret;}
与After的区别是要求原始方法执行过程中未出现异常现象
//@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象@AfterReturning("pt2()")public void afterReturning() {System.out.println("afterReturning advice ...");}
//@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行@AfterThrowing("pt2()")public void afterThrowing() {System.out.println("afterThrowing advice ...");}
任意业务层接口执行均可显示其执行效率(执行时长)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=123456
package com.itheima.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}
}
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;import javax.sql.DataSource;public class MybatisConfig {@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itheima.domain");ssfb.setDataSource(dataSource);return ssfb;}@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itheima.dao");return msc;}
}
import org.springframework.context.annotation.*;@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
package com.itheima.domain;import java.io.Serializable;public class Account implements Serializable {private Integer id;private String name;private Double money;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;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
public interface AccountDao {@Insert("insert into tbl_account(name,money)values(#{name},#{money})")void save(Account account);@Delete("delete from tbl_account where id = #{id} ")void delete(Integer id);@Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")void update(Account account);@Select("select * from tbl_account")List findAll();@Select("select * from tbl_account where id = #{id} ")Account findById(Integer id);
}
package com.itheima.service;import com.itheima.domain.Account;import java.util.List;public interface AccountService {void save(Account account);void delete(Integer id);void update(Account account);List findAll();Account findById(Integer id);
}
import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic void save(Account account) {accountDao.save(account);}@Overridepublic void delete(Integer id) {accountDao.delete(id);}@Overridepublic void update(Account account) {accountDao.update(account);}@Overridepublic List findAll() {return accountDao.findAll() ;}@Overridepublic Account findById(Integer id) {return accountDao.findById(id);}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTestCase {@Autowiredprivate AccountService accountService;@Testpublic void testFindById(){Account ac = accountService.findById(2);System.out.println(ac);}@Testpublic void testFindAll(){List all = accountService.findAll();System.out.println(all);}
}
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableAspectJAutoProxy
public class SpringConfig {
}
@Component
//切片类
@Aspect
public class ProjectAdvice {//匹配业务层的所有方法@Pointcut("execution(* com.itheima.service.*Service.*(..))")private void servicePt(){};@Around("ProjectAdvice.servicePt()")public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {//签名信息Signature signature = pjp.getSignature();//接口类型String className = signature.getDeclaringTypeName();//接口方法String methodName = signature.getName();//原始操作前来个记录时间long start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {//执行原始操作,不需要返回值了,直接查询10000遍pjp.proceed();}//结束后long end = System.currentTimeMillis();System.out.println("万次执行:"+className+"."+methodName+"---->"+(end-start)+"ms");}
}
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Overridepublic void save(Account account) {accountDao.save(account);}@Overridepublic void delete(Integer id) {accountDao.delete(id);}@Overridepublic void update(Account account) {accountDao.update(account);}@Overridepublic List findAll() {return accountDao.findAll() ;}@Overridepublic Account findById(Integer id) {return accountDao.findById(id);}
}
万次执行:com.itheima.service.AccountService.findAll---->2148ms
万次执行:com.itheima.service.AccountService.findById---->1280ms
@Component
@Aspect
public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")private void pt(){}//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数@Before("pt()")public void before(JoinPoint jp) {//用JoinPoint返回参数,返回的是数组Object[] args = jp.getArgs();System.out.println(Arrays.toString(args));System.out.println("before advice ..." );}}
正常运行是id为100,可以在中间更改参数,变成600
可以运用到参过来的参数有问题(例如数据格式、数据问题),可以处理
//ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用,直接getArgs//@Around("pt()")public Object around(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));//更改args数值,再作为参数扔进去args[0] = 666;Object ret = null;try {ret = pjp.proceed(args);} catch (Throwable t) {t.printStackTrace();}return ret;}
returning = "ret"与String ret以及JoinPoint和String同时存在
//设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同@AfterReturning(value = "pt()",returning = "ret")//JoinPoint和String同时存在,JoinPoint得放前面public void afterReturning(JoinPoint jp,String ret) {System.out.println("afterReturning advice ..."+ret);}
throwing = "t"与(Throwable t)
//设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同,即形参是返回对象@AfterThrowing(value = "pt()",throwing = "t")public void afterThrowing(Throwable t) {System.out.println("afterThrowing advice ..."+t);}
public String findName(int id,String password) {System.out.println("id:" + id);//故意来个异常,给AfterThrowing接收if (true) throw new NullPointerException();return "itcast";}
输出
id:100
afterThrowing advice ...java.lang.NullPointerException
Exception in thread "main" java.lang.NullPointerException
在业务方法执行之前对所有的输入参数进行格式处理——trim()
使用处理后的参数调用原始方法——环绕通知中存在对原始方法的调用
org.springframework spring-context 5.2.10.RELEASE org.springframework spring-jdbc 5.2.10.RELEASE org.springframework spring-test 5.2.10.RELEASE org.aspectj aspectjweaver 1.9.4 mysql mysql-connector-java 5.1.47 com.alibaba druid 1.1.16 org.mybatis mybatis 3.5.6 org.mybatis mybatis-spring 1.3.0 junit junit 4.12 test
@Configuration
@ComponentScan("com.itheima")
public class SpringConfig {
}
public interface ResourcesDao {boolean readResources(String url, String password);
}
@Repository
public class ResourcesDaoImpl implements ResourcesDao {public boolean readResources(String url, String password) {System.out.println(password.length());//模拟校验return password.equals("root");}
}
public interface ResourcesService {public boolean openURL(String url, String password);
}
@Service
public class ResourcesServiceImpl implements ResourcesService {@Autowiredprivate ResourcesDao resourcesDao;public boolean openURL(String url, String password) {return resourcesDao.readResources(url,password);}
}
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);ResourcesService resourcesService = ctx.getBean(ResourcesService.class);boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root");System.out.println(flag);}
}
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}
@Component
@Aspect
public class DataAdvice {@Pointcut("execution(boolean com.itheima.service.*Service.*(*,*))")private void servicePt(){}@Around("com.itheima.aop.DataAdvice.servicePt()")public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();for (int i = 0; i < args.length; i++) {//args是个对象,不一定是字符产,不用foreach循环,用fori,需要用到索引值判断是否是字符串// 判断参数是不是字符串if(args[i].getClass().equals(String.class)){//用toString转成字符串args[i] = args[i].toString().trim();}}//改完放回去哦Object ret = pjp.proceed(args);return ret;}
}
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);ResourcesService resourcesService = ctx.getBean(ResourcesService.class);boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root ");System.out.println(flag);}
}
4
true
在数据层保障一系列的数据库操作同失败同完成
spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
需求:实现任意两个账户间转账操作,A账户减钱,B账户加钱
public class JdbcConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Beanpublic DataSource dataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}
}
public class MybatisConfig {@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itheima.domain");ssfb.setDataSource(dataSource);return ssfb;}@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itheima.dao");return msc;}
}
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
public interface AccountDao {@Update("update tbl_account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update tbl_account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);
}
public interface AccountService {/*** 转账操作* @param out 传出方* @param in 转入方* @param money 金额*///配置当前接口方法具有事务@Transactionalpublic void transfer(String out,String in ,Double money) ;
}
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String out,String in ,Double money) {accountDao.outMoney(out,money);accountDao.inMoney(in,money);}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {@Autowiredprivate AccountService accountService;@Testpublic void testTransfer() throws IOException {accountService.transfer("Tom","Jerry",100D);}
}
如果中间出现异常,那么会出现A加,B不减的执行问题
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String out,String in ,Double money) {accountDao.outMoney(out,money);int i = 1/0;accountDao.inMoney(in,money);}}
因此在接口中开启事务
@Transactionalpublic void transfer(String out,String in ,Double money) ;
}
并且在JdbcConfig开启事务管理器
//配置事务管理器,mybatis使用的是jdbc事务,dataSource这个丢进去@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(dataSource);return transactionManager;}
并且在注解写上事务型驱动
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}
这样运行的时候就会保证同时成功或者失败,抛出异常java.lang.ArithmeticException: / by zero
outMoney事务和inMoney事务都会加入到transfer事务中,就可以实现,transfer下的操作同时成功或者失败,不会出现inMoney事务出错,而outMoney事务不回滚的问题了
transfer叫做事务管理员,outMoney事务和inMoney事务叫做事务协调员
事务管理员就是发起事务方,就是发起事务的那个方法,例如transfer
事务协调员就是加入事务方,可以是数据层方法也可以是业务层方法