千家信息网

Spring[02.基础知识整理(下)]

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,Bean使用外部属性文件在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等). 而这些部署细节实际上需要和 Bean 配置相分
千家信息网最后更新 2024年09月22日Spring[02.基础知识整理(下)]

Bean使用外部属性文件

  • 在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等). 而这些部署细节实际上需要和 Bean 配置相分离

  • Spring 提供了一个 PropertyPlaceholderConfigurer 的 BeanFactory 后置处理器, 这个处理器允许用户将 Bean 配置的部分内容外移到属性文件中. 可以在 Bean 配置文件里使用形式为 ${var} 的变量, PropertyPlaceholderConfigurer 从属性文件里加载属性, 并使用这些属性来替换变量.

  • xml context加载外部配置文件

Spel表达式

是一个支持运行时查询和操作对象图的强大的表达式语言

通过 SpEL 可以实现:

  • 通过 bean 的 id 对 bean 进行引用
  • 调用方法以及引用对象中的属性
  • 计算表达式的值
  • 正则表达式的匹配

案例

验证邮箱

^[_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)"+"*@[A-Za-z0-9]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$

Bean的生命周期

生命周期

  • Spring IOC 容器可以管理 Bean 的生命周期, Spring 允许在 Bean 生命周期的特定点执行定制的任务.
  • Spring IOC 容器对 Bean 的生命周期进行管理的过程:
    • 通过构造器或工厂方法创建 Bean 实例
    • 为 Bean 的属性设置值和对其他 Bean 的引用
    • 调用 Bean 的初始化方法
    • Bean 可以使用了
    • 当容器关闭时, 调用 Bean 的销毁方法
  • 在 Bean 的声明里设置 init-method 和 destroy-method 属性, 为 Bean 指定初始化和销毁方法.

Bean后置处理器

基本介绍
  • Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理.
  • Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例. 其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.
  • 对Bean 后置处理器而言, 需要实现BeanPostProcessor接口.

在初始化方法被调用前后, Spring 将把每个 Bean 实例分别传递给上述接口的以下两个方法:

public class MyBeanPostProcesser implements BeanPostProcessor {    // 在init-method之前调用    @Override    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {        System.out.println("postProcessBeforeInitialization:  " + s);        return o;    }    // 在init-method之后调用    @Override    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {        System.out.println("postProcessAfterInitialization:  " + s);        return o;    }}
后置处理器特性

//Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理.
//Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理, 而非单一实例.
// 其典型应用是: 检查 Bean 属性的正确性或根据特定的标准更改 Bean 的属性.

//采用前置和后置处理之后
//首先运行构造函数
//然后给属性赋值
//然后访问前置处理函数
//然后访问init函数
//然后访问后置处理函数
//然后输出结果
//最后访问destroy函数

@Autowired 自动装配 Bean

  • 构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解
    默认情况下, 所有使用 @Authwired 注解的属性都需要被设置. 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false

  • 默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时, 通过类型的自动装配将无法工作. 此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter 已指定注入 Bean 的名称

  • @Authwired 注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配.

  • @Authwired 注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean.

  • @Authwired 注解用在 java.util.Map 上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值

AOP与AspectJ

概述

是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.

AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.

在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.

AOP 的好处:

  • 每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
  • 业务模块更简洁, 只包含核心业务代码.

术语

  • 切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
  • 通知(Advice): 切面必须要完成的工作
  • 目标(Target): 被通知的对象
  • 代理(Proxy): 向目标对象应用通知之后创建的对象
  • 连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
  • 切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

AspectJ

AspectJ:Java 社区里最完整最流行的 AOP 框架.在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP

依赖包:

  • aopalliance-1.0.jar
  • aspectjrt.jar
  • aspectjtools.jar
  • aspectjweaver.jar
  • org.aspectj.matcher.jar

AspectJ通知注解

  • @Before: 前置通知, 在方法执行之前执行
  • @After: 后置通知, 在方法执行之后执行
  • @AfterRunning: 返回通知, 在方法返回结果之后执行
  • @AfterThrowing: 异常通知, 在方法抛出异常之后
  • @Around: 环绕通知, 围绕着方法执行

    切入点合并

  • @Pointcut 将多个被限制的bean或者bean方法合并为一个集合,AspectJ通知注解可以直接调用合并切入点的方法,
    如: combinePointCut

    Order

  • 制定切面的优先级
  • 数字越小,代表优先级越高

案例

@Aspect@Order(0)public class AspectProcessor {    // 合并切入点    @Pointcut("execution(* com.demo.aop.aspect.CalculatorImpl.*(..)))")    public void combinePointCut(){}    // 此函数在指定的bean方法执行前响应    // @Before("execution (* com.demo.aop.aspect.CalculatorImpl.*(..))")    @Before("combinePointCut()")    public void beforeAspectProcessor(JoinPoint joinPoint){        System.out.println("beforeAspectProcessor method name: " + joinPoint.getSignature().getName());        System.out.println("beforeAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));    }    // 此函数在指定的bean方法执行后响应    // @After("execution (* com.demo.aop.aspect.CalculatorImpl.*(..))")    @After("combinePointCut()")    public void afterAspectProcessor(JoinPoint joinPoint){        System.out.println("afterAspectProcessor method name: " + joinPoint.getSignature().getName());        System.out.println("afterAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));    }//    此函数在指定的bean方法执行前、后响应//    @Around("combinePointCut()")//    public void around(ProceedingJoinPoint pjp) throws Throwable{//        System.out.println("已经记录下操作日志@Around 方法执行前");//        pjp.proceed();//        System.out.println("已经记录下操作日志@Around 方法执行后");//    }    // 如果只想在连接点返回的时候记录, 应使用返回通知代替后置通知    @AfterReturning(value = "combinePointCut()", returning = "result")    public void afterReturningAspectProcessor(JoinPoint joinPoint, Object result){        System.out.println("afterReturningAspectProcessor method name: " + joinPoint.getSignature().getName());        System.out.println("afterReturningAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));        System.out.println("afterReturningAspectProcessor method result: " + result);    }    // 异常通知    @AfterThrowing(value = "combinePointCut()", throwing = "e")    public void afterThrowingAspectProcessor(JoinPoint joinPoint, Exception e){        System.out.println("afterThrowingAspectProcessor method name: " + joinPoint.getSignature().getName());        System.out.println("afterThrowingAspectProcessor method args: " + Arrays.asList(joinPoint.getArgs()));        System.out.println("afterThrowingAspectProcessor method throwing: " + e);    }}
  • 将 aop Schema 添加到 根元素中.
  • 要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素
  • 当 Spring IOC 容器侦测到 Bean 配置文件中的 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理



通过代理方式实现AspectJ

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Repository;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Arrays;/** * Created by plusplusxu on 2017/10/23. * * 动态代理模式主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法, * 在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法), * 因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象, * 具体应用的话,比如可以添加调用日志,做事务控制等。 * 动态代理是设计模式当中代理模式的一种。 */@Repositorypublic class CalculatorImplProxy {    @Autowired    private Calculator target;    public Calculator getProxy(){        Calculator proxy = null;        // 代理对象由哪一个加载器加载        ClassLoader loader = target.getClass().getClassLoader();        // 代理对象的内容        Class[] interfaces = new Class[]{Calculator.class};        // 当调用代理对象的方法的时候,该方法被执行        InvocationHandler h = new InvocationHandler() {            @Override            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                // 代理对象方法执行前操作                System.out.println("method: " + method.getName() + "  " + "  args:  " + Arrays.asList(args));                Object result = method.invoke(target, args);                // 代理对象方法执行后操作                System.out.println("method: " + method.getName() + "  " + "  args:  " + Arrays.asList(args) + "  result: " + result);                return result;            }        };        proxy = (Calculator)Proxy.newProxyInstance(loader, interfaces, h);        return proxy;    }}

JDBC

JdbcTemplate

作为 Spring JDBC 框架的核心, JDBC 模板的设计目的是为不同类型的 JDBC 操作提供模板方法. 每个模板方法都能控制整个过程, 并允许覆盖过程中的特定任务. 通过这种方式, 可以在尽可能保留灵活性的情况下, 将数据库存取的工作量降到最低.

  • 每次使用都创建一个 JdbcTemplate 的新实例, 这种做法效率很低下.
  • JdbcTemplate 类被设计成为线程安全的, 所以可以再 IOC 容器中声明它的单个实例, 并将这个实例注入到所有的 DAO 实例中.
  • JdbcTemplate 也利用了 Java 1.5 的特定(自动装箱, 泛型, 可变长度等)来简化开发

JdbcDaoSupport

该类声明了 jdbcTemplate 属性, 它可以从 IOC 容器中注入, 或者自动从数据源中创建.

package com.mysql;import org.springframework.jdbc.core.BatchPreparedStatementSetter;import org.springframework.jdbc.core.JdbcTemplate;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.List;/** * Created by plusplusxu on 2017/10/25. * * 操作mysql的数据类 * * JdbcTemplate 类被设计成为线程安全的, 所以可以再 IOC 容器中声明它的单个实例, 并将这个实例注入到所有的 DAO 实例中. */public class ProjectDao {    public ProjectDao(JdbcTemplate jdbcTemplateObject) {        this.jdbcTemplateObject = jdbcTemplateObject;    }    public ProjectDao() {    }    public JdbcTemplate getJdbcTemplateObject() {        return jdbcTemplateObject;    }    public void setJdbcTemplateObject(JdbcTemplate jdbcTemplateObject) {        this.jdbcTemplateObject = jdbcTemplateObject;    }    // spring 提供的访问数据库的工具类,需要配置javax.sql.DataSource    // 本例中在applicationContext中配置的mysql数据源    private JdbcTemplate jdbcTemplateObject;    // 添加一条项目记录    // tbl_devops_project为vsdo数据库的项目表(Project)    public void addproject(Project project) {        String sql = "INSERT INTO tbl_devops_project(name,public,language,description," +                "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " +                "VALUES(?,?,?,?,?,?,?,?,?,?)";        try {            jdbcTemplateObject.update(sql, project.getName(),project.getPub(),project.getLanguage(),project.getDescription(),                    project.getAdmin(),project.getMembers(),project.getGitlab_id(),                    project.getGitlab_url(),project.getCode_styles(),project.getSonar_lint_server_url());        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 根据项目名删除记录    public void delprojectbyname(String name) {        String sql = "DELETE FROM tbl_devops_project WHERE name=?";        try {            jdbcTemplateObject.update(sql,name);        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 删除所有项目    public void delallproject() {        String sql = "DELETE FROM tbl_devops_project";        try {            jdbcTemplateObject.update(sql);        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 更新某个项目    public void updproject(Project project) {        String sql = "UPDATE tbl_devops_project set description=? WHERE name=?";        try {            jdbcTemplateObject.update(sql,project.getDescription(), project.getName());        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 获取所有项目    public List allproject() {        List projects = null;        String sql = "SELECT * FROM tbl_devops_project";        try {            // ProjectMapper 项目表的映射类            projects = jdbcTemplateObject.query(sql, new ProjectMapper());        }        catch (Exception e){            System.out.println(e);        }        return projects;    }    // 查询项目名对应的记录    public List queryprojectbyname(String name) {        List projects = null;        String sql = "SELECT * FROM tbl_devops_project WHERE name=?";        try {            projects = jdbcTemplateObject.query(sql, new Object[]{name}, new ProjectMapper());        }        catch (Exception e){            System.out.println(e);        }        return projects;    }    // 打印所有项目    public void displayall(){        List projects = allproject();        for(Project s : projects){            System.out.println(s);        }    }    // 批量插入    public void insertBatch(final List projects){        String sql = "INSERT INTO tbl_devops_project(name,public,language,description," +                "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " +                "VALUES(?,?,?,?,?,?,?,?,?,?)";        jdbcTemplateObject.batchUpdate(sql, new BatchPreparedStatementSetter() {            @Override            public void setValues(PreparedStatement ps, int i) throws SQLException {                Project project = projects.get(i);                ps.setString(1, project.getName());                ps.setBoolean(2, project.getPub());                ps.setString(3, project.getLanguage());                ps.setString(4, project.getDescription());                ps.setString(5, project.getAdmin());                ps.setString(6, project.getMembers());                ps.setInt(7, project.getGitlab_id());                ps.setString(8, project.getGitlab_url());                ps.setString(9, project.getCode_styles());                ps.setString(10, project.getSonar_lint_server_url());            }            @Override            public int getBatchSize() {                return projects.size();            }        });    }}
package com.mysql;import org.springframework.jdbc.core.BatchPreparedStatementSetter;import org.springframework.jdbc.core.support.JdbcDaoSupport;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.List;/** * Created by plusplusxu on 2017/10/26. * * 继承于JdbcDaoSupport,直接封装了JdbcTemplate,写起来更方便 * * 该类声明了 jdbcTemplate 属性, 它可以从 IOC 容器中注入, 或者自动从数据源中创建. * */public class JdbcDaoSupportImpl extends JdbcDaoSupport {    // 添加一条项目记录    // tbl_devops_project为vsdo数据库的项目表(Project)    public void addproject(Project project) {        String sql = "INSERT INTO tbl_devops_project(name,public,language,description," +                "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " +                "VALUES(?,?,?,?,?,?,?,?,?,?)";        try {            this.getJdbcTemplate().update(sql, project.getName(),project.getPub(),project.getLanguage(),project.getDescription(),                    project.getAdmin(),project.getMembers(),project.getGitlab_id(),                    project.getGitlab_url(),project.getCode_styles(),project.getSonar_lint_server_url());        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 根据项目名删除记录    public void delprojectbyname(String name) {        String sql = "DELETE FROM tbl_devops_project WHERE name=?";        try {            this.getJdbcTemplate().update(sql,name);        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 删除所有项目    public void delallproject() {        String sql = "DELETE FROM tbl_devops_project";        try {            this.getJdbcTemplate().update(sql);        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 更新某个项目    public void updproject(Project project) {        String sql = "UPDATE tbl_devops_project set description=? WHERE name=?";        try {            this.getJdbcTemplate().update(sql,project.getDescription(), project.getName());        }        catch (Exception e){            System.out.println(e);        }        return ;    }    // 获取所有项目    public List allproject() {        List projects = null;        String sql = "SELECT * FROM tbl_devops_project";        try {            // ProjectMapper 项目表的映射类            projects = this.getJdbcTemplate().query(sql, new ProjectMapper());        }        catch (Exception e){            System.out.println(e);        }        return projects;    }    // 查询项目名对应的记录    public List queryprojectbyname(String name) {        List projects = null;        String sql = "SELECT * FROM tbl_devops_project WHERE name=?";        try {            projects = this.getJdbcTemplate().query(sql, new Object[]{name}, new ProjectMapper());        }        catch (Exception e){            System.out.println(e);        }        return projects;    }    // 打印所有项目    public void displayall(){        List projects = allproject();        for(Project s : projects){            System.out.println(s);        }    }    // 批量插入    public void insertBatch(final List projects){        String sql = "INSERT INTO tbl_devops_project(name,public,language,description," +                "admin, members, gitlab_id,gitlab_url,code_styles,sonar_lint_server_url) " +                "VALUES(?,?,?,?,?,?,?,?,?,?)";        this.getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() {            @Override            public void setValues(PreparedStatement ps, int i) throws SQLException {                Project project = projects.get(i);                ps.setString(1, project.getName());                ps.setBoolean(2, project.getPub());                ps.setString(3, project.getLanguage());                ps.setString(4, project.getDescription());                ps.setString(5, project.getAdmin());                ps.setString(6, project.getMembers());                ps.setInt(7, project.getGitlab_id());                ps.setString(8, project.getGitlab_url());                ps.setString(9, project.getCode_styles());                ps.setString(10, project.getSonar_lint_server_url());            }            @Override            public int getBatchSize() {                return projects.size();            }        });    }}
                                                                                                                                

事务

事务管理是企业级应用程序开发中必不可少的技术, 用来确保数据的完整性和一致性.

事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用

事务的四个关键属性(ACID)

  • 原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
  • 一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
  • 隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
  • 持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.

Spring 既支持编程式事务管理, 也支持声明式的事务管理.

  • 编程式事务管理: 将事务管理代码嵌入到业务方法中来控制事务的提交和回滚. 在编程式管理事务时, 必须在每个事务操作中包含额外的事务管理代码.
  • 声明式事务管理: 大多数情况下比编程式事务管理更好用. 它将事务管理代码从业务方法中分离出来, 以声明的方式来实现事务管理. 事务管理作为一种横切关注点, 可以通过 AOP 方法模块化. Spring 通过 Spring AOP 框架支持声明式事务管理.

用 @Transactional 注解声明式地管理事务

Spring 还允许简单地用 @Transactional 注解来标注事务方法.

  • 为了将方法定义为支持事务处理的, 可以为方法添加 @Transactional 注解. 根据 Spring AOP 基于代理机制, 只能标注公有方法.

  • 可以在方法或者类级别上添加 @Transactional 注解. 当把这个注解应用到类上时, 这个类中的所有公共方法都会被定义成支持事务处理的.
  • 在 Bean 配置文件中只需要启用 元素, 并为之指定事务管理器就可以了.
  • 如果事务处理器的名称是 transactionManager, 就可以在 元素中省略 transaction-manager 属性. 这个元素会自动检测该名称的事务处理器.

事务的传播行为

当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行.

0