为了完全实现Annotation的方式开发,将bean.xml取代,首先需要有可以扫描全局的注解
@ComponentScan(basePackages = {"com.kali"})还要让spring知道你的配置类是哪个
这就需要第二个注解:@Configuration,对于runner和dataSource在xml中采用的时Ioc的
方式,所以观察其结构
@Bean用于把当前方法的返回值作为bean对象存入spring的ioc容器管理。
当我们使用注解配置方法时,如果方法有参数,spring框架会自动到容器中查找
看有没有可用的bean对象。查找方式和@Autowired的作用一样
@Configuration不是什么时候都可以不写的,只有当作为AnnotationConfigApplicationContext()
的参数时才可以不写,如果AnnotationConfigApplicationContext的参数中并列写上两个配置类的
Class不添加@Configuration、依然可以实现,那么就成了兄弟关系,其实SpringConfiguration
是想作为主配置的
@import用于导入其它配置类的字节码,当使用@import注解时,有@import注解的类就是父配置类,
导入的则是子配置类,显然父子的配置关系是合理的。
@PropertySource:
为了解决读取配置文件,并通过@Value为属性注入
@PropertySource(value = {"classpath:jdbcConfig.properties"})
这个注解用于指定properties文件的位置
value属性指定文件的位置,关键字classpath表示类路径
小结:
基于xml的开发方式和基于annotation的方式,怎么方便怎么来,有时基于全注解的方式开发反而增加了工作量
xml和annotation结合的方式也挺好
spring是如何选择不同的数据源的:
这个问题归根结底是被注解修饰的方法如果有参数,那么参数的对的对象是如何选择具有多个
匹配情况下的选择。
使用@Qualifier来解决,当方法参数出现多个符合注入的匹配情况时,使用这个注解来指定在冲突情况下选择的注入
对象。例如不同的数据源的使用,出现无法精确匹配时就可以使用此注解
JDBC配置文件最好是这样的格式:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=super
我尝试取掉前面的jdbc.前缀,结果数据库一直延迟连接,最后失败,只是报了超时
Spring整合Junit配置
1.导入Spring整合Junit的坐标
2.使用Junit提供的一个注解将Junit的main方法替换成Spring提供的
@RunWith(SpringJUnit4ClassRunner.class)
3.告知spring运行器,spring的ioc创建是基于xml还是注解的,说明位置
基于xml:@ContextConfiguration(classes = {SpringBeanConfiguration.class})
classpath代表是类路径下
基于annotation:@ContextConfiguration(locations = {"classpath:bean.xml"})
locations属性代表位置
classes属性代表配置类位置
Spring的事务管理
public void transfer(String sourceName, String targetName, double money) {
User source = userDao.findByName(sourceName);//获取连接
User target = userDao.findByName(targetName);//获取连接
source.setMoney(source.getMoney() - money);
target.setMoney(target.getMoney() + money);
userDao.updateUser(source);//获取连接
userDao.updateUser(target);//获取连接
}
每个Connection都有自己的独立事务,为了让这几个独立的事务组合成一个事务,使用
ThreadLocal把Connection和当前线程绑定,使一个线程中只有一个能控制的事务对象
事务管理回到业务层service
1.转账过程:查询转出账户、转入账户、转出账户余额减少、转入账户余额增加,更新转出、
更新转入账户这几步操作。如果在两个更新操作之间发生异常可能导致余额不翼而飞,多以
要对事务进行控制。
2.分析原因:转账过程中总共由四次连接,而每一次数据库连接都有其单独的事务控制,所以这是
四个单独的原子控制组成的一个逻辑操作,那么要将四个单独的事务控制在一个连接中解决就可以
保证事务的原子性。
3.如何操作:将一个链接绑定在一个线程上,那么这个线程的所有操作就是一个连接在操作,也就能
保证事务
小结:在事务控制权的转变过程中,我们将数据源交给了ConnectionUtils来管理。dao层执行时先到工具类
中当前线程本地尝试获取连接,如果有连接则说明上一个执行的方法和将要执行的方法属于同一个事务控制下的,
否则,则说明是第一次操作。然后由事务控制类进行事务控制。
SpringAop
基于注解的Aop配置,识别expression需要AspectJ Jar包支持
开启Aop
@EnableAspectJAutoProxy注解开启Aop
@Aspect切面
@Pointcut("execution(* com.kali.service.impl.*.*(..))")
private void point(){} //在普通方法上声明全局表达式
@Before("point()")
@AfterReturning("point()")
@AfterThrowing("point()")
@After("point()")
@Around("point()")
Aspect:切入点和通知的结合就是切面,通知是切点前后的操作
SpringAop的配置:
AspectJ是解析表达式的
join point指的是那些拦截的点,指的是方法,spring只支持方法类型的连接点
point cut指对哪些join point进行拦截的定义
1.包通知交给spring管理
2.使用aop:config标签表明aop的配置
3.使用aop:aspect标签配置切面
id 给切面一个唯一标识
ref 指定通知类bean的id
4.在aop:aspect标签内使用对应标签配置通知类型
aop:before
aop:after
aop:around
pointcut = "execution()"将通知和切入点关联
5.execution表达式的写法:
修饰符 返回值 全限定路径+方法名
全局通配写法: * *..*.*(..)
表达式书写规则:
访问修饰符可以省略
包名 * 匹配任意包,但有几级就得有几个*
可以写 *..代表当前包及子包
(..)代表有无参数都可匹配
(*)代表有参数
实际开发中表达式的写法:* com.kali.service.impl.*.*(..)
前置通知:在切点前执行
后置通知:在切点正常执行后执行
异常通知:在切点执行中发生异常后执行
最终通知:无论切点是否正常执行都会执行
环绕通知:当我们配置了环绕通知却没有执行切入点方法,只执行了通知方法
分析原因:再写到那个太代理的时候对代理方法做了增强。
通过对比动态代理的环绕通知,发现动态代理的环绕通知中有明确的切点方法调用。
我们的却没有。
解决方法:
spring提供了一个ProceedingJoinPoint接口,提供类一个proceed方法
就相当于明确调用切入点方法,该接口可作为环绕通知方法的参数,执行时
框架为我们提供实现类使用
小结:前置通知在切点前执行,后置通知在切点正确执行后执行异常通知在切点发生异常
时执行最终通知无论方法是否正确执行都会执行。环绕通知可以将四种通知融合形成完整的
如动态代理的环绕通知。
尤其着重@Around
【动态代理本身就是一个环绕通知:有前置、后置、异常、最终、切点】
@Around("point()")
public Object around(ProceedingJoinPoint joinPoint){
Object result = null;
try {
pre();
//明确调用业务层方法(aop标签中配置的切入点方法)
result = joinPoint.proceed(joinPoint.getArgs());
afterReturn();
} catch (Throwable throwable) {
afterThrowing();
throwable.printStackTrace();
}finally {
after();
}
return result;
}
切面:通知+切点
Spring的JdbcTemplate类似DbUtils
1.准备数据源 负责与数据库建立连接
使用spring提供的数据源DriverManagerDataSource
2.创建JdbcTemplate
JdbcTemplate jt = new JdbcTemplate();
设置数据源给jt,也可传参
jt.setDataSource(ds);
3.对于有结果集的返回用RowMapper