Bean生命周期
小思考-CarService内DemoService是否有值?
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(mainConfig.class);
CarService demo1 = context.getBean("carService", CarService.class);
CarService demo2 = new CarService();
@Service
public class CarService {
@Autowired
private DemoService demoService; //是否有值
public void say(){
System.out.println("day day up!");
}
}
@Service
public class CarService {
private DemoService demoService;
public CarService(DemoService demoService) { //是否有值
this.demoService = demoService;
}
}
创建过程
- 利用该类的构造方法来实例化获得普通对象(推断构造方法)
- 判断该类中是否有@Autowired等注解,进行属性赋值(DI)
- 注入后,该对象是否实现BeanNameAware、BeanClassLoaderAware
- 初始化前(@PostConstruct)
- 初始化(实现InitializingBean)
- 初始化后(AOP 代理对象)
- 生成Bean
推断构造方法
若一个类中有多个构造方法,spring将通过推断构造方法进行选择,得到对象。
判断逻辑:
- 若类中只有一个构造方法,不管是无参还是有参构造方法,都会使用这个构造方法。
- 若类中有多个构造方法,存在无参构造方法spring就用无参构造方法。不存在无参构造方法,spring会报错。
- 若在某个构造方法上添加@Autowired注解,spring会使用这个构造方法
例:
@Service
public class CarService {
private DemoService demoService;
//无参构造方法
public CarService() {
}
//有参构造方法1
public CarService(DemoService demoService) {
this.demoService = demoService;
}
//有参构造方法2
@Autowired
public CarService(DemoService demoService1,DemoService demoService) {
this.demoService = demoService;
}
}
@Autowired注解
@Autowired是sping提供,按byType(类型装配)自动注入
类型匹配一个bean,则无须关注名字
类型匹配多个bean,则通过名字进行查找,也可以使用@Qualifier进行Bean指定
carService.testService();//调用CarService类的方法
@Bean
public DemoService demoService(){
return new DemoService();
}
@Bean
public DemoService demoService1(){
return new DemoService();
}
@Bean
public DemoService demoService2(){
return new DemoService();
}
@Service
public class CarService {
//这里通过类型会获取三个Bean demoService,demoService1,demoService2
//可以根据名字进行匹配 或者 使用 @Qualifier("demoService")
@Autowired
private DemoService demoService1;
public void testService(){
demoService1.say();
}
}
代理模式
代理模式(proxy)是一种设计模式,提供了一种对目标对象的访问方式,好处:可以在实现目标对象的基础上,增强对目标对象的操作,扩展目标对象的功能。
代理类继承目标类,对调用目标类
AOP基本流程
- 生成代理类CarServiceProxy,代理类继承CarService
- CarServiceProxy重写CarService类中方法,比如CarService中sayHello方法
- 代理类中携带target对象即普通对象,用target普通对象调用普通对象的方法。
carService.sayHello(); //这里的carService对象就是代理对象
@Component
@Aspect
public class CarAop {
@Pointcut("execution(* com.mec.deom.CarService.*(..))")
public void listen(){}
@Before(value = "listen()")
public void pointcutTest(JoinPoint joinPoint){
//打印的target对象为CarService普通对象
System.out.println(joinPoint.getTarget() instanceof CarService ? ((CarService) joinPoint.getTarget()) : null);
System.out.println("-------------");
//执行完后,会执行target.sayHello
}
}
//最后通过target调用sayHello
public void sayHello(){
System.out.println("day day up!");
}
事务Transactation
//添加@Configuration事务才可以生效---事务管理器和jdbcTemplate需要是同一个数据源(dataSource)
@Bean
public JdbcTemplate jdbcTemplate(){
return new JdbcTemplate(dataSource());
}
@Bean
public PlatformTransactionManager transactionManager(){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource());
return dataSourceTransactionManager;
}
@Bean
public DataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
driverManagerDataSource.setUrl("jdbc:mysql://localhost:3306/test?setUnicode=true&characterEncoding=utf8");
driverManagerDataSource.setUsername("root");
driverManagerDataSource.setPassword("dev123456");
return driverManagerDataSource;
}
流程
- 扫描@Transactional
- 创建一个数据库连接(事务管理器 dataSource)
- 修改conn.autocommit = false
- 执行sql-- target普通方法调用方法
- commit/rollback
事务传播行为
- propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
- propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
- propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
- propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
- propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的
事务传播失效场景
carService.testService();
//当前例子并未抛异常
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void testService(){
jdbcTemplate.execute("insert into user (name,age) values ('test',19)");
sayHello();
}
//Propagation.NEVER 当前事务存在则抛异常
@Transactional(propagation = Propagation.NEVER)
public void sayHello(){
System.out.println("day day up!");
}
解决方案
@Service
public class CarService {
@Autowired
private JdbcTemplate jdbcTemplate;
//新建类
@Autowired
private BaseCarService baseCarService;
@Transactional
public void testService(){
jdbcTemplate.execute("insert into user (name,age) values ('test',19)");
baseCarService.sayHello();
}
}
@Component
public class BaseCarService {
@Transactional(propagation = Propagation.NEVER)
public void sayHello(){
System.out.println("day day up!");
}
}
//异常展示
Exception in thread "main" org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
总结:
一、代理对象来进行事务管理器进行数据库连接,修改autocommit属性,而调用到testService方法时却是普通对象的调用,因此事务传播失效。
二、新建BaseCarService类,当carService普通对象调用sayHello方法时,spring会在IOC容器中找到拥有事务的BaseCarServiceProxy代理类。