启动类
- 启动类
- @SpringBootApplication包含
- @ComponentScan等价于<context:component-scan base-package="com.rockjh"/>
- basePackages基础扫描包
- includeFilters,excludeFilters是用于包含和排除的筛选器
Bean注入
-
@Configuration、@Bean、@Scope
- @Configuration相当于Beans标签,主要用于配置类
- @Bean相当与Bean标签,所属类必须有@Configuration或@Component才有效
- @Scope用于定义bean的生存范围
- singleton全局有且仅有一个实例(默认)
- prototype原型模式,每次获取Bean都会获取一个新的实例
- request,session,global session不常用,使用他们的时候需要初始化web的时候做出配置
-
@Component、@Service、@Controller、@RestController、@Repository、@Conditional、@PropertySource、@Primary
- @Component用来标识一个Bean,被修饰的class会被IOC加载为spring bean
- @Service对应的是业务层的Bean
- @Controller对应的是控制层的Bean
- @RestController对应的是控制层的Bean,等同于@ResponseBody和@Controller的组合
- @Repository对应的是数据访问层的Bean
- @Conditional按照一定的条件进行判断,满足条件给容器注册bean,用于class、method上,一个注解可以同时有多个条件,条件类实现Condition接口
- @PropertySource用于导入配置文件,使用classpath:/xxx、file:/分别指定类路径和文件系统,可以和@Configuration和@ConfigurationProperties联用,将属性注入到当前组件中,@ConfigurationProperties可以指定前缀
- @Primary使用@Autowire自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者
-
@Autowired、@Resource、@Qualifier,@Lookup,@Value
- @Autowired按照类型注入Bean,默认required=true
- @Qualifier可以和@Autowired联合使用,达到按照beanName注入的目的
- @Resource可以按照name和type注入,如果name和type都没有指定,默认按照name注入
- @Lookup当组件scope属性是singleton时,如果要想注入一个新的实例,可以在一个抽象方法中使用该注解,返回新的实例
- @Value可以用来获取配置文件中的值@Value("
{name}}")可以配合@PropertySource获取propertes文件中的数据,使用示例如下:
//properties文件内容 remote.path={\ station_map_point_task:{'url':'/api/v1/scene/rect_monitor_obj','method':'post'}\ } //类的注解配置 @Component @PropertySource(value="classpath:remote_path_priperties") //通过@Value获取值 @Value("#{${remote.path}}") private Map<String,Map<String,String>> pathMap;
复合注解
Component annotation = AnnotationUtils.findAnnotation(this.getClass(), Component.class),实现原理是循环目标类的注解,然后对比再循环,@Service被@Component修饰,所以annotation有值
@Import
允许通过他引入@Configuration注解的类,引入ImportSelector接口和ImportBeanDefinitionRegistrar接口实现的类,定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link * * ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
在springboot中对@Import的处理方式
-
判断如果被Import的是ImportSelector接口的实现,会初始化被引用的这个类,然后调用它的selectImports方法去获得所需要的引入的configuration,然后递归处理
-
判断如果被Import的是importBeanDefinitionRegistrar接口的实现,那么初始化后,将当前对象委托给这个ImportBeanDefinitionRegistrar(猜测的)
-
如果引用的是一个正常的Component,这样可以在BeanFactory中得到bean
@AliasFor
- 可以注解到自定义注解中的两个属性上,表示互为别名,这时候两个属性的值是同步的,指定其中一个,另外一个也是一样的值,如果两个都指定,指定的两个值一定要相同,否则会报错
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Inherited public @interface MyAnnotation { @AliasFor(attribute = "location") String value() default ""; @AliasFor(attribute = "value") String location() default ""; } - 继承父注解的属性,可以拥有更加强大的功能
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Inherited @MyAnnotation public @interface SubMyAnnotation { //指定的和父类注解的location属性一致 @AliasFor(value = "location", annotation = MyAnnotation.class) String subLocation() default ""; //缺省指明继承的父注解的中的属性名称,则默认继承父注解中同名的属性名 @AliasFor(annotation = MyAnnotation.class) String value() default ""; }
@AliasFor注解是spring注解,只能通过AnnotationUtils工具类中的方法获取才有效
Transactional
-
@EnableTransactionManagement启用注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
-
@Transactional修饰public的class和method来开启事务,它里面有一些属性,可以定义事务的隔离级别,传播行为以及是否只读,回滚异常等,简单说一下隔离级别和传播行为
1.事务隔离级别
- TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
- TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别
2.事务传播行为
- TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
使用示例
//事务传播行为为TransactionDefinition.PROPAGATION_REQUIRED //事务隔离级别为TransactionDefinition.ISOLATION_DEFAULT @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT) -
@TransactionalEventListener
在项目中,往往需要执行数据库操作后,发送消息或事件来异步调用其他组件来执行相应的操作,例如:用户注册成功后,发送邮件。@TransactionalEventListener注解就可以完美的实现
@Service("fooService") public class FooServiceImpl implements FooService { private static final Logger LOGGER = Logger.getLogger(FooServiceImpl.class); @Override public void insertFoo(Foo foo) throws MyTransactionException { LOGGER.info("[fooService] start insert foo"); ApplicationEventPublisher eventPublisher = EventPublisher.getApplicationEventPublisher(); if (null != eventPublisher) { eventPublisher.publishEvent(new MyTransactionEvent("test", this)); } LOGGER.info("[fooServive] finish insert foo"); } }public class MyTransactionEvent extends ApplicationEvent { private String name; public MyTransactionEvent(String name, Object source) { super(source); this.name = name; } public String getName() { return this.name; } }@Slf4j @Component public class MyTransactionListener { @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void hanldeOrderCreatedEvent(MyTransactionEvent event) { LOGGER.info("transactionEventListener start"); // do transaction event LOGGER.info("event : " + event.getName()); // finish transaction event LOGGER.info("transactionEventListener finish"); } }
AOP
在IOC装载bean时,会调用AspectJAwareAdvisorAutoProxyCreator中的postProcessAfterInitialization方法,来处理aop的问题,在代码中aop视为拦截器,如果有多个aop,就是拥有多个拦截器,会判断当前bean是否被增强,如果被增强会生成代理,实际注入的bean就是加入了代理之后的字节码,常用注解:
- @Aspect切面标识注解,需要配合@Component使用
- @Pointcut定义切面,如下几个例子
- execution(* *(..))表示匹配所有方法
- execution(public * com. savage.service.UserService.*(..)) 表示匹配com.savage.server.UserService中所有的公有方法
- execution(* com.savage.server...(..))表示匹配com.savage.server包及其子包下的所有方法
- @Before前置通知, 在方法执行之前执行
- @After后置通知, 在方法执行之后执行
- @AfterRunning返回通知, 位于@After之后执行,在方法返回结果之后执行
- @AfterThrowing异常通知, 在方法抛出异常之后执行
- @Around环绕通知, 围绕着方法执行,需要在这里主动执行增强之前的代码
生成字节码使用的JdkDynamicAopProxy或CglibAopProxy
Profile
在application.yml文件中配置如下,会读取application-dev.yml文件,pro生产环境同理
spring:
profiles:
active: dev
Exception
异常处理在controller中进入到结束之前发生异常时,对异常进行拦截,处理异常的返回值回遵循之前的顺序写入到response中,实现统一的异常处理,避免项目内部的异常堆栈映射到页面中,示例代码如下
@Slf4j
@ControllerAdvice
public class HandlerException {
/**
* 全局异常处理
*
* @return
*/
@ExceptionHandler({Exception.class})
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity handlerGlobalException(Exception e) {
log.error("system internal error",e)
return ResponseUtil.fail();
}
}