基于廖雪峰老师网站所做的java学习笔记。为了快速审计java代码,而非开发~

IOC

Ioc容器,负责把一些service和datasource包装起来,并且管理创建和销毁

Bean, 一些service,例如用户注册,登陆,邮箱发送。。。这些会反复用到的.定义用[Annotation]((https://www.liaoxuefeng.com/wiki/1252599548343744/1282382596407330),即@Component 注解的方式, 和 @Autowired注解,将指定Bean注入到指定字段中。其他注解:

  • @Configuration : 配置类,包括创建第三方Bean
  • @ComponentScan: 配置类同时会用这个Annotation,用于自动搜索当前类所在的包以及子包,把所有标注为@Component的Bean自动创建出来,并根据@Autowired进行装配。
  • @Autowired(required = false) 注入Bean,但如果没有则忽略
  • @PostConstruct@PreDestroy : Bean的垃圾清理机制,对清理方法标记

Bean系统中一个数据库事务中被调用,则使用 @Transactional 注解
除此之外,还有Prototype的Bean,需要 @Scope注解,第三方Bean,则需要 @Bean
注解。
注入配置(.properties文件):
注入方法1: 使用 @PropertySource注解(例如@PropertySource("app.properties")),在@Configuration`配置类上加注解。

@Value("${smtp.port:25}")
    private int port;

上述,@Value("${smtp.port:25}") 的用法为读取默认值
注入方法2: 在Bean上直接用 @Value("${smtp.port:25}") , 然后,也可以用 @Value("#{smtpConfig.host}")读取Bean下的host值(没有注解的变量也可以)。#{}表示从JavaBean读取属性
此外,java分不同的环境,例如开发(native/dev),测试(test),生产(prod),则需要用条件装配:

@Configuration
@ComponentScan
public class AppConfig {
    @Bean
    @Profile("!test")
    ZoneId createZoneId() {
        return ZoneId.systemDefault();
    }

    @Bean
    @Profile("test")
    ZoneId createZoneIdForTest() {
        return ZoneId.of("America/New_York");
    }
}

除此之外,还可以用 @Conditional 决定是否创建某个Bean, 案例见这个,还有:

  • @ConditionalOnProperty(name="app.smtp", havingValue="true")
  • @ConditionalOnClass(name = "javax.mail.Transport")

AOP

AOP包括以下概念:

  • Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点;
  • Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行;
  • Pointcut:切入点,即一组连接点的集合;
  • Advice:增强,指特定连接点上执行的动作;
  • Introduction:引介,指为一个已有的Java对象动态地增加新的接口;
  • Weaving:织入,指将切面整合到程序的执行流程中;
  • Interceptor:拦截器,是一种实现增强的方式;
  • Target Object:目标对象,即真正执行业务的核心逻辑对象;
  • AOP Proxy:AOP代理,是客户端持有的增强后的对象引用。
    通过AOP可以将指定的方法装配到指定Bean的前后

Aspect相关注解/拦截器类型
拦截器:

  • @Aspect : 表明是一个Aspect
  • @Before: 标注的方法在注入到指定Service的每个public方法执行前
  • @Around : 标注的方法在注入到指定Service的每个public方法执行后

案例还是看教程写的
同时,例如于配置的类,需要加注解 @EnableAspectJAutoProxy 表示自动查找Aspect的Bean
另外这里还可以自定义注解, 任何有@MetricTIme注解的方法, 则调用 metric() 方法:

@Aspect
@Component
public class MetricAspect {
    @Around("@annotation(metricTime)")
    public Object metric(ProceedingJoinPoint joinPoint, MetricTime metricTime) throws Throwable {
        String name = metricTime.value();
        long start = System.currentTimeMillis();
        try {
            return joinPoint.proceed();
        } finally {
            long t = System.currentTimeMillis() - start;
            // 写入日志或发送至JMX:
            System.err.println("[Metrics] " + name + ": " + t + "ms");
        }
    }
}

数据库

jdbc
spring提供了使用jdbc访问数据库的方法,例如用 @PropertySource("jdbc.properties") 读取数据库配置文件,同样也是加载配置类上的。spring还提供了 JdbcTemplate的方式:

return jdbcTemplate.execute((Connection conn) -> {

	// ...
}
return jdbcTemplate.execute("SELECT * FROM users WHERE name = ?", (PreparedStatement ps) -> {
	// ...
}

更多的jdbcTemplate的数据库查询案例见原文

事务
spring为了同时支持jdbc和jta(分布式)两种事务类型,抽象出 PlatformTransactionManager, 除了在配置类下,定义出PlatformTransactionManger外,还要加 @EnableTransactionManagement 用于支持声明式事务。
使用到事务的方法,则加一个 @Transactional注解 / 或加在类上,表示任何public方法都支持事务

  • 需要回滚的事务注解:@Transactional(rollbackFor = {RuntimeException.class, IOException.class})
  • 复杂的事务场景:需要定义事务传播模型

集成Hibernate
实现orm到java对象互换

JPA
一个ORM标准,但只是提供接口,依旧需要一些”实现“,例如Hibernate就是JPA的实现

Hibernate和JPA需要用到SessionFactoryEntityManagerFactory

Proxy模式
为了获取Bean的更改及时同步到数据库,需要创建代理类。例如一些实现用户注册,登陆的User类,让UserProxy继承User类,同时这个代理类必须保持session,事务提交session关闭。但,为了关闭后,依旧获取事务一致的数据,引入了Attached/Datached状态,用于记录Bean是否在session范围内。
ORM还提供了多级缓存,用于多次查询返回同一实例

全自动ORM/半自动ORM

全自动ORM相对于jdbcTemplate有以下差别:

  1. 查询后需要手动提供Mapper实例以便把ResultSet的每一行变为Java对象;
  2. 增删改操作所需的参数列表,需要手动传入,即把User实例变为[user.id, user.name, user.email]这样的列表,比较麻烦。
    jdbcTemplate有确定性,缺点是代码繁琐,ORM则有多个缓存,在二级缓存增大了数据的不一致性,可能会意外更新。所以产生了半自动ORM
    半自动ORM框架常见的就是MyBatis,

MyBaitis
需要创建 SqlSessionFactoryBean

@Bean
SqlSessionFactoryBean createSqlSessionFactoryBean(@Autowired DataSource dataSource) {
    var sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);
    return sqlSessionFactoryBean;
}```
另外,Mybatis使用Mapper来实现映射,且Mapper必须是接口。例如
```java
public interface UserMapper {
	@Select("SELECT * FROM users WHERE id = #{id}")
	User getById(@Param("id") long id);
}

如果插入不需要传入 id 但是需要获取插入后的主键,需要加 @Options注解。
使用 @MapperScan自动创建Mapper实现类,将多个不同的Mapper接口自动创建接口实现。