Spring 四要点 IOC DI AOP jdbc+事务
IOC (控制反转) 三种
三种方式注册bean xml 注解 java类
https://www.cnblogs.com/wuchanming/p/5426746.html
@Component @Controller @Service @Repository
java类 @Configuration @Bean
DI (依赖注入) 三种
1) xml 自动注入 四种方式:
byName – 根据属性名称自动装配
byType – 按数据类型自动装配
constructor – 在构造函数参数的byType方式。
autodetect – 先constructor 没有用 byType
2) xml 手动注入:
ref 对象 value 值
"" 填写属性名字
数组或者List集合
set
使用
// 使用Spring的工厂:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过工厂获得类:
UserService userService = (UserService) applicationContext.getBean("userService");
userService.sayHello();
spring 整合junit测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo{
@Resource(name="userService")
private UserService userService;
@Test
public void demo2(){
userService.save();
}
}
AOP (面向切面编程) 原理两种 方式两种
Srping框架的AOP技术底层也是采用的代理技术,代理的方式提供了两种
1) 基于JDK的动态代理
* 必须是面向接口的,只有实现了具体接口的类才能生成代理对象
2) 基于CGLIB动态代理
* 对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式
如果实现类接口,使用JDK动态代理完成AOP
如果没有实现接口,继承了类,采用CGLIB动态代理完成AOP
JDK的动态代理
public class MyProxyUtils {
public static UserDao getProxy(final UserDao dao) {
// 使用Proxy类生成代理对象
UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(),
dao.getClass().getInterfaces(), new InvocationHandler() {
// 代理对象方法一执行,invoke方法就会执行一次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){
System.out.println("记录日志...");
// 开启事务
}
// 提交事务
// 让dao类的save或者update方法正常的执行下去
return method.invoke(dao, args);
}
});
// 返回代理对象
return proxy;
}
}
CGLIB的代理技术
public static OrderDaoImpl getProxy(){
// 创建CGLIB核心的类
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(OrderDaoImpl.class);
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
if("save".equals(method.getName())){
// 记录日志
System.out.println("记录日志了...");
}
return methodProxy.invokeSuper(obj, args);
}
});
// 生成代理对象
OrderDaoImpl proxy = (OrderDaoImpl) enhancer.create();
return proxy;
}
两种方式:
xml 和 注解
xml配置
1) 编写切面类
@Component
public class MyAspectXml {
// 定义通知
public void log(){
System.out.println("记录日志...");
}
}
2) 配置AOP 以下都是AspectJ框架的AOP方式 不是Spring自带的AOP,后者了解即可
切面类 切面类方法 切入点表达式
在配置切入点的时候,需要定义表达式,格式如下:execution(public * *(..)),具体展开如下:
* 切入点表达式的格式如下:
* execution([修饰符] 返回值类型 包名.类名.方法名(参数))
* 修饰符可以省略不写,不是必须要出现的。
* 返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
* 包名例如:com.itheima.demo3.BookDaoImpl
* 首先com是不能省略不写的,但是可以使用 * 代替
* 中间的包名可以使用 * 号代替
* 如果想省略中间的包名可以使用 ..
* 类名也可以使用 * 号代替,也有类似的写法:*DaoImpl
* 方法也可以使用 * 号代替
* 参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用 ..
通知类型
1. 前置通知
* 在目标类的方法执行之前执行。
* 配置文件信息:
* 应用:可以对方法的参数来做校验
2. 最终通知
* 在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
* 在配置文件中编写具体的配置:
* 应用:例如释放资源
3. 后置通知
* 方法正常执行后的通知。
* 在配置文件中编写具体的配置:
* 应用:可以修改方法的返回值
4. 异常抛出通知
* 在抛出异常后通知
* 在配置文件中编写具体的配置:
* 应用:包装异常的信息
5. 环绕通知
* 方法的执行前后执行。
* 在配置文件中编写具体的配置:
* 要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行。
注解
* @Aspect -- 定义切面类的注解
* @Before -- 前置通知
* @AfterReturing -- 后置通知
* @Around -- 环绕通知(手动执行)
* @After -- 最终通知
* @AfterThrowing -- 异常抛出通知
配置切入点表达式
@Aspect
public class MyAspectAnno {
@Before(value="execution(public void com.lzz.demo.CustomerDaoImpl.save())")
public void log(){
System.out.println("记录日志...");
}
}
在配置文件中开启自动代理
Spring自身的AOP方式链接
https://www.yiibai.com/spring/spring-aop-examples-advice.html
JDBC + 事务
JDBC
JdbcTemplate Spring框架提供的
HibernateTemplate Spring框架整合Hibernate框架
1) 自己new
@Test
public void run1(){
// 创建连接池,先使用Spring框架内置的连接池
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 创建模板类
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 完成数据的添加
jdbcTemplate.update("insert into t_account values (null,?,?)", "测试",10000);
}
2) 交给IOC
选择一个连接池
* Spring自带的数据库连接池
* DBCP连接池
* C3P0连接池
* Spring管理模板类
jdbcTemplate 在DAO层注入
或者继承JdbcDaoSupport类 注入DataSource 用getJdbcTemplate()得到
事务 三种
编程式事务 手动编写代码
声明式事务 基于AspectJ的XML方式和注解方式
PlatformTransactionManager 平台事务管理器 需要注入DataSource
TransactionDefinition 事务定义信息(事务的隔离级别,传播行为,超时,只读)
TransactionStatus 事务的状态
1) 编程式事务
1. 步骤一:配置一个事务管理器,Spring使用PlatformTransactionManager接口来管理事务,所以需要使用到他的实现类
2. 步骤二:配置事务管理的模板
3. 步骤三:在需要进行事务管理的类中,注入事务管理的模板.
4. 步骤四:在业务层使用模板管理事务:
// 注入事务模板对象
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void pay(final String out, final String in, final double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 扣钱
accountDao.outMoney(out, money);
int a = 10/0;
// 加钱
accountDao.inMoney(in, money);
}
});
}
2) 声明式事务
xml的方式
1. 步骤一:配置事务管理器
2. 步骤二:配置事务增强
3. 步骤三:配置AOP的切面
* 注意:如果是自己编写的切面,使用标签,如果是系统制作的,使用标签。
4. 步骤四:编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void run(){
accountService.pay("lzz", "美美", 1000);
}
}
注解的方式
1. 步骤一:配置事务管理器
2. 步骤二:开启注解事务
3. 步骤三:在业务层上添加一个注解:@Transactional
4. 编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
@Resource(name="accountService")
private AccountService accountService;
@Test
public void run(){
accountService.pay("lzz", "美美", 1000);
}
}
@Transactional 使用细节
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
虽然 @Transactional注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。
另外,@Transactional注解应该只被应用到public方法上,这是由Spring AOP 的本质决定的。如果你在protected、private 或者默认可见性的方法上使用@Transactional注解,这将被忽略,也不会抛出任何异常。
默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
关于Spring事务的隔离级别节
Dao层配置事务 需要Service层调用 那么Service相当于A Dao相当于B
* PROPAGATION_REQUIRED(默认值) -- A中有事务,使用A中的事务.如果没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!
* PROPAGATION_SUPPORTS -- A中有事务,使用A中的事务.如果A中没有事务.那么B也不使用事务.
* PROPAGATION_MANDATORY -- A中有事务,使用A中的事务.如果A没有事务.抛出异常.
* PROPAGATION_REQUIRES_NEW(记)-- A中有事务,将A中的事务挂起.B创建一个新的事务.(保证A,B没有在一个事务中)
* PROPAGATION_NOT_SUPPORTED -- A中有事务,将A中的事务挂起.
* PROPAGATION_NEVER -- A中有事务,抛出异常.
* PROPAGATION_NESTED(记) -- 嵌套事务.当A执行之后,就会在这个位置设置一个保存点.如果B没有问题.执行通过.如果B出现异常,运行客户根据需求回滚(选择回滚到保存点或者是最初始状态)