栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring的简单使用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring的简单使用

Spring

学习Spring的笔记

概述
1.Spring是轻量级的开源的JavaEE框架;    
2.Spring可以解决企业应用开发的复杂性;    
3.Spring有两个核心部分:IOC和AOP:        
    1)IOC:控制反转,把创建对象的过程交给Spring进行管理;        
    2)AOP:面向切面,不修改源代码进行功能增强;   
4.Spring特点:        
    1)方便解耦,简化开发;        
    2)AOP编程支持;        
    3)方便程序测试;        
    4)方便和其他框架进行整合;        
    5)方便进行事务操作;        
    6)降低API开发难度;
Spring核心模块
1.beans:IOC    
2.Core::IOC    
3.Context:上下文    
4.Expression:表达式

             org.springframework         spring-beans    5.2.6.RELEASE    org.springframework    spring-context    5.2.6.RELEASE    org.springframework    spring-core    5.2.6.RELEASE    org.springframework    spring-expression    5.2.6.RELEASE
    
commons-logging    commons-logging    1.1.1    
junit    junit    RELEASE
test

IOC 什么是IOC
(1)控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理;
(2)使用IOC的目的,为了耦合度降低;
IOC底层原理
(1)XML解析;
(2)工厂模式;
(3)反射;
IOC接口
(1)IOC思想基于IOC容器完成,IOC容器底层就是对象工厂;
(2)Spring提供IOC容器实现两种方式:(两个接口)
        1)BeanFactory:IOC容器基本实现,是Spring内部的使用接口,一般不提供给开发人员进行使用;
           *加载配置文件的时候不会创建对象,在获取或使用对象的时候才去创建对象;
        2)ApplicationContext:BeanFactory接口的子接口,提供了更多更强大的功能,一般是开发人员使用的;
           *加载配置文件时候就会把配置文件对象进行创建;
(3)ApplicationContext的主要实现类:
        1)FileSystemXmlApplicationContext:需要指定文件的盘符路径;
        2)ClassPathXmlApplicationContext:需要指定类路径;
IOC操作Bean管理 什么是Bean管理?

Bean管理->两个操作:
(1)Spring创建对象;
(2)Spring注入属性;
Bean管理操作->两种方式:

1.基于XML配置文件实现方式

基于XML方式创建对象



(1)在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建;
(2)在bean标签里面有很多属性:

  • id属性:唯一标识
    
  • class属性:创建对象所在类的全路径
    

(3)创建对象,默认使无参构造方法

基于XML方式注入属性
(1)DI:依赖注入,就是注入属性;

  • 第一种注入方式:使用set方法进行注入;
    

    
    
    


可以简化为P名称空间注入,可以简化基于XML配置方式:

  1. 添加P名称空间


  1. 进行属性注入
  • 第二种注入方式:使用有参构造方法进行注入;
    

    
    
    
    
    


3.IOC操作Bean管理(xml注入其他类型属性)

  • 字面量
  1. null值
    
    


  1. 属性值包含特殊符号
    



    
>]]>

  • 外部Bean
    (1)创建两个类service和dao,在service调用dao里面的方法

    



  • 内部Bean
    (1)一对多关系:如部门和员工;
// 员工类
public class Emp {    
private String ename;    
private String gender;   
// 员工属于某一个部门    
private Dept dept;    
public void setDept(Dept dept) {        this.dept = dept;    }    
public void setEname(String ename) {        this.ename = ename;    }    
public void setGender(String gender) {        this.gender = gender;    }
}

//部门类
public class Dept {    
private String dname;    
public void setDname(String dname) {        this.dname = dname;    }
}

xml配置


    
    
    
    
    
        
    
        
                
        


  • 级联赋值

    
    
    
    
    


    



另一种写法,需要相关属性的get方法

// 员工属于某一个部门
private Dept dept;public Dept getDept() {    return dept;}

    
    
    
    
    
    

    


  • 注入数组集合,List集合,Map集合,Set集合
public class Stu {    // 数组类型属性    
private String[] courses;   
// List集合类型属性   
private List list;    
// Map类型属性    
private Map maps;    
// Set集合属性    
private Set sets;    
public void setSets(Set sets) {        this.sets = sets;    }    
public void setList(List list) {        this.list = list;    }    
public void setMaps(Map maps) {        this.maps = maps;    }    
public void setCourses(String[] courses) {        this.courses = courses;    }}

    
        
        
            
                C++
                数据结构
            
        
        
        
            
                火力少年王
                四驱兄弟
            
        
        
        
            
                
                
            
        
        
        
            
                来玩啊
                就不来
            
        
    

在集合里面设置对象类型值:


    
        
        
            
                C++
                数据结构
            
        
        
        
            
                火力少年王
                四驱兄弟
            
        
        
        
            
                
                
            
        
        
        
            
                来玩啊
                就不来
            
        
        
        
            
                
                
            
        
    
    
    
        
    
    
        
    

把集合注入部分提取出来:先在spring配置文件中引入名称空间util,然后再注入

public class Books {

    private List list;

    public void setList(List list) {
        this.list = list;
    }

}

    
        九阴白骨爪
        九阳神功
        降龙十八掌
    
    
    
        
    

工厂Bean

  • Spring有两种类型bean,一种普通bean,一种工厂bean(FactoryBean)
  • 普通bean:在配置文件中定义bean类型就是返回类型;
  • 工厂bean:在配置文件定义bean类型可以和返回类型不一样;
    第一步:创建类,让这个类作为工厂bean,实现接口FactoryBean
    第二步:实现接口里面的方法,在实现的方法中定义返回bean类型;
public class MyBean implements FactoryBean {
    // 定义返回bean
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("aaa");
        return course;
    }
    public Class getObjectType() {
        return null;
    }
    public boolean isSingleton() {
        return false;
    }
}

Bean的作用域
1.在Spring里面,默认情况下,bean是单实例对象;
2.在Spring里面设置创建bean实例是单实例还是多实例;
在bean标签里的scope属性:


    

singleton:单实例,加载Spring配置文件时候就会创建一个单实例对象;
prototype:多实例,不是在加载Spring配置文件的时候就创建对象,而是在调用getBean方法时候才会创建一个新的实例对象;
request:一次请求,每次创建对象会放在request域对象中;
session:一次会话,每次创建对象会放在session域对象中;

Bean生命周期
生命周期:从对象创建到对象销毁的过程

  • 通过构造器创建bean实例(无参构造方法)
  • 为bean的属性设置值和其他bean的引用(调用set方法)
  • 把bean的实例传递给bean后置处理器的方法postProcessBeforeInitialization
  • 调用bean的初始化的方法(需要进行配置初始化方法)
  • 把bean的实例传递给bean后置处理器的方法postProcessAfterInitialization
  • bean可以使用了(对象获取到了)
  • 当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
public class Orders {
    private String oname;
    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("set方法......属性设置");
    }
    public Orders() {
        System.out.println("无参构造......实例化");
    }
    // 创建执行的初始化方法
    public void initMethod(){
        System.out.println("初始化方法......");
    }
    // 创建执行的销毁方法
    public void destroy(){
        System.out.println("销毁方法......");
    }
}
// 实现接口BeanPostProcessor,创建后置处理器
public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的......"+beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的......"+beanName);
        return bean;
    }
}

        
    
    
    
@Test
    public void testBean7(){
        //加载Spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean8.xml");
        //获取配置创建的对象
        per.xgt.bean.Orders orders = context.getBean("orders", per.xgt.bean.Orders.class);
        System.out.println(orders);
        ((ClassPathXmlApplicationContext)context).close();
    }
无参构造......实例化
set方法......属性设置
在初始化之前执行的......orders
初始化方法......
在初始化之后执行的......orders
per.xgt.bean.Orders@553a3d88
销毁方法......

Bean管理XML方式(自动装配)
自动装配:根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入;

  • 根据属性名称自动注入

    
        
    
    

问题:如果根据类型,那么相同类型的Bean不能配置多个,否则报错;

Bean管理XML方式(引入外部属性文件)

  • 德鲁伊连接池,模拟配置

            com.alibaba
            druid
            1.1.9
        
  • 直接配置连接池

    
        
        
        
        
    
  • 引入外部属性文件配置连接池
    创建外部的properties文件,配置数据库连接信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root

把外部properties属性文件引入到Spring配置文件中


    
    
    
        
        
        
        
    
2.基于注解方式实现
  • Component
  • Service
  • Controller
  • repository
    上面的四个注解功能是一样的,都可以用来创建bean实例

基于注解方式实现对象创建
需要引入一个额外依赖:AOP


    org.springframework
    spring-aop
    5.2.6.RELEASE
 

开启组件扫描


    

开启组件扫描的细节:


    
        
    
    
        
    

创建类,在类上面添加创建对象注解,value值等于xml方式的id,可以省略不写,默认值为类名称首字母小写

// value值等于xml方式的id,可以省略不写,默认值为类名称首字母小写
@Service(value = "userService")
public class StudentServiceImpl {
    public void add(){
        System.out.println("service add......");
    }
}

基于注解方式实现属性注入

  • @Autowired:根据属性类型进行自动装配
    (1)创建Service和dao对象,在service和dao类添加创建对象注解
    (2)在service注入dao,service类添加dao类型属性,在属性上面使用注解@Autowired
@Repository(value = "studentDao")
public class StudentDaoImpl implements StudentDao {
    @Override
    public void add() {
        System.out.println("studentDao add......");
    }
}
// value值等于xml方式的id,可以省略不写,默认值为类名称首字母小写
@Service(value = "studentService")
public class StudentServiceImpl {

    @Autowired
    private StudentDao studentDao;

    public void add(){
        studentDao.add();
        System.out.println("service add......");
    }
}
  • @Qualifier:根据属性名称进行注入
    (1)@Qualifier需要和@Autowired一起使用
@Service(value = "studentService")
public class StudentServiceImpl {

    @Autowired
    @Qualifier("studentDao")
    private StudentDao studentDao;

    public void add(){
        studentDao.add();
        System.out.println("service add......");
    }
}
  • @Resource:可以根据类型,也可以根据名称注入
@Service(value = "studentService")
public class StudentServiceImpl {

//    @Autowired
//    @Qualifier("studentDao")
//    private StudentDao studentDao;

    @Resource(name = "studentDao")
    private StudentDao studentDao;

    public void add(){
        studentDao.add();
        System.out.println("service add......");
    }
}

**PS:**Resource属于包:javax.annotation.Resource,所以spring官方更推荐使用@Autowired和@Qualifier

  • @Value:注入普通类型属性
@Value("哈哈哈哈")
    private String name;

完全注解开发例子
(1)创建配置类,替代xml配置文件

@Configuration // 作为配置类,替代XML配置文件
@ComponentScan(basePackages = {"per.xgt"},
        useDefaultFilters = false,
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
        }
)
public class SpringConfig {

}
// 包扫描两种情况
@Configuration // 作为配置类,替代XML配置文件
@ComponentScan(basePackages = {"per.xgt"},
        useDefaultFilters = true,
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
        }
)
public class SpringConfig {

}

(2)编写测试类

@Test
    public void test1(){
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        StudentServiceImpl studentService = context.getBean("studentService", StudentServiceImpl.class);
        System.out.println(studentService);
        studentService.add();
    }
AOP 概述

(1)面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间耦合度降低,提高程序的可重用性,同时提高了开发的效率;
(2)通俗讲:不通过修改源代码方式,在主干功能里面添加新功能;

AOP底层原理

1.AOP底层使用动态代理
(1)有两种情况动态代理:

  • 有接口情况:使用JDK动态代理
    =》创建接口实现类代理对象
    =》通过代理对象,增强功能
  • 没有接口情况:使用CGLIB动态代理
    =》创建当前类的子类代理对象
    =》通过子类增强逻辑
JDK动态代理实现

调用newProxyInstance方法:
=》ClassLoader loader:类加载器;
=》Class[] interfaces:增强方法所在类的所实现的接口,支持多个接口;
=》InvocationHandler h:实现这个接口,InvocationHander,创建代理对象,写增强的方法;

实现

  • 创建接口,定义方法
public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}
  • 创建接口实现类
public class UserDaoImpl implements UserDao{
    @Override
    public int add(int a, int b) {
        return a+b;
    }
    @Override
    public String update(String id) {
        return id;
    }
}
  • 使用Proxy类创建接口代理对象
public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces = {UserDao.class};
        // 创建接口实现类的代理对象
//        Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//            // 匿名内部类实现
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                return null;
//            }
//        });
        UserDao userDao = new UserDaoImpl();
        UserDao user = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int add = user.add(5, 10);
        System.out.println(add);
    }
}

// 创建代理对象代码
class UserDaoProxy implements InvocationHandler{
    // 1.把创建的是谁的dialing对象,把谁传递过来;
    // 通过有参构造传递
    private Object obj;
    public UserDaoProxy(Object obj){
        this.obj = obj;
    }

    // 增强逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法之前
        System.out.println("方法之前执行......" + method.getName() + "......传递的参数:" + Arrays.toString(args));

        // 被增强的方法执行
        Object invoke = method.invoke(obj, args);

        // 方法之后
        System.out.println(obj);

        // 方法返回
        return invoke;
    }
}
相关术语
  • 连接点:能被增强的方法;
  • 切入点:实际被增强的方法;
  • 通知(增强):实际增强的逻辑部分;
    1.前置通知:
    2.后置通知:
    3.环绕通知:
    4.异常通知:
    5.最终通知:
  • 切面:把增强应用到切入点的过程;
Spring实现AOP

Spring框架一般都是基于AspectJ实现AOP操作
AspectJ:不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring一起使用,进行AOP操作;

基于AspectJ实现AOP操作

Spring的Aspects依赖



    org.springframework
    spring-aspects
    5.2.6.RELEASE

因为AspectJ独立于Spring所以还需要如下依赖



    net.sourceforge.cglib
    com.springsource.net.sf.cglib
    2.2.0



    org.aspectj
    aspectjweaver
    1.6.8
    runtime



    aopalliance
    aopalliance
    1.0

切入点表达式
作用:知道对哪个类里面的哪个方法进行增强;
语法结构:Execution( [权限修饰符] [返回类型] [类的全路径] 方法名称 )
例1:对per.xgt.dao.UserDao 类里面add方法增强
execution(* per.xgt.dao.UserDao.add(…))
例2:对per.xgt.dao.UserDao 类里面所有方法增强
execution(* per.xgt.dao.UserDao.(…))
例3:对per.xgt.dao 包里面所有类里面的所有方法增强
execution(
per.xgt.dao..(…)))

基于注解方式实现
  • 创建类,在类里面定义方法
public class User {
    public void add(){
        System.out.println("add......");
    }   
}
  • 创建增强类
    在增强类里面,创建方法,让不同方法代表不同通知类型
public class UserProxy {
    // 前置通知
    public void before(){
        System.out.println("before......");
    }
}
  • 进行通知的配置

在Spring配置文件中,开启注解扫描


使用注解创建User和UserProxy对象

@Component

在增强的类上添加注解@Aspect

@Component
@Aspect
public class UserProxy {
    // 前置通知
    public void before(){
        System.out.println("before......");
    }
}

在Spring配置文件中,开启生成代理对象


    
  • 配置不同类型的通知
    在增强类的里面作为通知的方法上面添加通知类型的注解,并且使用切入点表达式配置
@Component
@Aspect
public class UserProxy {

    @Before(value = "execution(* per.xgt.aopAnnotation.User.add(..))")
    // 前置通知
    public void before(){
        System.out.println("before......");
    }

    @After(value = "execution(* per.xgt.aopAnnotation.User.add(..))")
    public void after(){
        System.out.println("after......");
    }

    @AfterReturning(value = "execution(* per.xgt.aopAnnotation.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning......");
    }

    @AfterThrowing(value = "execution(* per.xgt.aopAnnotation.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing......");
    }

    @Around(value = "execution(* per.xgt.aopAnnotation.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("aroundBefore......");

        // 被增强的方法执行
        proceedingJoinPoint.proceed();

        System.out.println("aroundAfter......");
    }

}

完全注解开发可以将注解里面内容替换成:

@Configuration
@ComponentScan(basePackages = {"per.xgt.aopAnnotation"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class MyConfig {
}
通知类型
  • @Before:前置通知

  • @After:最终通知:在方法执行之后执行,无论方法是否发生异常都会执行;

  • @AfterReturning:后置通知,返回通知:在方法返回结果之后执行,如果方法有异常就不会执行;

  • @AfterThrowing:异常通知:

  • @Around:环绕通知

没有异常执行结果:

aroundBefore......
before......
add......
aroundAfter......
after......
afterReturning......

有异常执行结果

aroundBefore......
before......
add......
after......
afterThrowing......

java.lang.ArithmeticException: / by zero

对相同切入点进行抽取

//相同切入点抽取
    @Pointcut(value = "execution(* per.xgt.aopAnnotation.User.add(..))")
    public void pointCut1(){}

    @Before(value = "pointCut1()")
    // 前置通知
    public void before(){
        System.out.println("before......");
    }

有多个增强类对同一个方法进行增强,设置优先级
在增强类的上面添加注解@Order(数字类型值),数字类型值越小优先级越高

@Component
@Aspect
@Order(1)
public class PersonProxy {
    @Before(value = "execution(* per.xgt.aopAnnotation.User.add(..))")
    // 前置通知
    public void before(){
        System.out.println("PersonProxy.before......");
    }
}
PersonProxy.before......
aroundBefore......
before......
add......
aroundAfter......
after......
afterReturning......
基于XML配置文件实现
  • 创建增强类与被增强类,创建方法
public class Book {
    public void buy(){
        System.out.println("buy......");
    }
}
public class BookProxy {
    public void before(){
        System.out.println("before......");
    }
}
  • 在Spring配置文件中创建两个类对象

    
  • 在Spring配置文件中配置切入点

    
        
        
        
        
            
        
    
JDBCTemplate

Spring框架对JDBC进行封装,使用JDBCTemplate方便实现对数据库操作;

相关依赖

        
        
        
            org.springframework
            spring-jdbc
            5.2.6.RELEASE
        
        
        
        
            org.springframework
            spring-tx
            5.2.6.RELEASE
        
        
        
        
            org.springframework
            spring-orm
            5.2.6.RELEASE
        
        
        
        
            mysql
            mysql-connector-java
            8.0.29
        
        
        
        
            com.alibaba
            druid
            1.1.9
        
配置数据库连接池

因为MYSQL8.0后需要驱动包版本更新,所以:
1.com.mysql.jdbc.Driver 更换为 com.mysql.cj.jdbc.Driver;
2.不需要建立 SSL 连接的,需要显式关闭–》useSSL=false;
3.最后还需要设置 CST。也就是设置时区–》serverTimezone=UTC;


        
        
        
        
    
JDBCTemplate对象注入DataSource

    
        
    
JDBCTemplate的简单使用
  • 创建service类,创建dao类,在dao注入jdbcTemplate对象
@Service
public class BookService {
    @Autowired
    private BookDao bookDao;
}

public interface BookDao {
}

@Repository
public class BookDaoImpl implements BookDao {
    // 注入JDBCTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

开启注解扫描


  • 创建表以及对应实体类
CREATE TABLE `test_book`  (
  `test_bookid` int NOT NULL AUTO_INCREMENT,
  `test_bookname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `test_bookstatus` int NULL DEFAULT NULL,
  PRIMARY KEY (`test_bookid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
public class Book {

    private int bookId;
    private String bookName;
    private int bookStatus;

    @Override
    public String toString() {
        return "Book{" +
                "bookId=" + bookId +
                ", bookName='" + bookName + ''' +
                ", bookStatus=" + bookStatus +
                '}';
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public int getBookStatus() {
        return bookStatus;
    }

    public void setBookStatus(int bookStatus) {
        this.bookStatus = bookStatus;
    }
}
  • 添加操作
    调用jdbcTemplate.update();第一个参数是SQL语句,第二是个可变参数,设置SQL语句中的位置参数;
@Override
    public void add(Book book) {
        // sql
        String sql = "insert into test_book values(null,?,?)";
        // 调用方法实现,返回值是影响行数
        int update = jdbcTemplate.update(sql, book.getBookName(), book.getBookStatus());
        System.out.println(update);
    }
  • 修改和删除与新增是一样的,只是SQL不一样,且在传入参数的地方可以用数组的方式传入;

  • 查询返回某个值
    第一个参数是sql,第二个参数是返回值类型;

@Override
    public int selectCount() {
        String sql = "SELECT count(1) from test_book";
        Integer i = jdbcTemplate.queryForObject(sql, Integer.class);
        return i;
    }
  • 查询返回某个对象
    第一个参数:sql;第二个参数:是接口,返回不同类型数据,使用这个接口里面实现类完成数据的封装;第三个参数是未知数值;
    注意:返回封装的实体类属性名必须和查询结果列名相匹配
@Override
    public Book selectOne(int id) {
        String sql = "SELECT test_bookid bookId,test_bookname bookName,test_bookStatus bookStatus from test_book where test_bookid = ?;";
        Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Book.class), id);
        return book;
    }
  • 查询返回集合
@Override
    public List selectList() {
        String sql = "SELECT test_bookid bookId,test_bookname bookName,test_bookStatus bookStatus from test_book;";
        List books = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Book.class));
        return books;
    }
  • 批量添加
    batchUpdate():有两个参数:第一个参数:sql语句;第二个参数:修改的多条数据;
@Override
    public int[] batchAdd(List batchArgs) {
        String sql = "insert into test_book values(null,?,?)";
        // 返回值为int[],数组长度为影响行数
        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
        return ints;
    }
  • 批量修改和删除与添加类似只是SQL不同;
Spring事务

事务:数据库操作最基本单元,逻辑上的一组操作,要么都成功,如果有一个失败那么所有的操作都失败;

事务四个特性(ACID)
  • 原子性:要么都成功,要么都失败;
  • 一致性:操作之前和操作之后的总量是不变的;
  • 隔离性:多事务操作时,事务之间不会产生影响;
  • 持久性:事务提交后永久生效;
事务传播行为

propagation:当一个事务方法被另一个事务方法调用时,这个事务方法如何进行;
定义多重传播行为:
默认REQUIRED;

  1. REQUIRED:如果有事务在运行,当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行;
  2. REQUIRED_NEW:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务在运行,应该将它挂起;
  3. SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中;
  4. NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  5. MANDATORY:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常;
  6. NEVER:当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常;
  7. NESTED:如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行,嵌套的事务可以独立于当前事务运行。如果当前事务不存在,那么其行为与REQUIRED一样:各个厂商对这种传播行为支持有差异;
事务隔离级别

isolation:事务特性隔离性,多事务操作之间不会产生影响;
问题:
1.脏读:一个未提交的事务读读取到了另一个未提交的数据;
2.不可重复读:一个未提交的事务,读取到了提交事务修改数据;
3.幻读:一个未提交的事务读取到了另一条事务添加数据;
解决问题:通过事务的隔离级别解决问题;
1.READ_UNCOMMITTED读未提交:不能解决问题;
2.READ_COMMITTED读已提交:解决脏读;
3.REPEATABLE_READ可重复度:解决不可重复读;
4.SERIALIZABLE串行化:解决所有问题
Spring默认使用数据库使用的隔离级别,Mysql默认是可重复读

环境搭建
  • 表结构和初始数据
DROP TABLE IF EXISTS `test_account`;
CREATE TABLE `test_account`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `userName` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `money` int NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
INSERT INTO `test_account` VALUES (1, 'lucy', 1000);
INSERT INTO `test_account` VALUES (2, 'mary', 1000);
  • 创建service,搭建dao,完成创建和对象注入

        
        
        
        
    

    
    
        
    

    
@Repository
public class MoneyDaoImpl implements MoneyDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void addMoney() {
        String sql = "update test_account set money = money + ? where userName = ?";
        int i = jdbcTemplate.update(sql, 100, "mary");
    }

    @Override
    public void reduceMoney() {
        String sql = "update test_account set money = money - ? where userName = ?";
        int i = jdbcTemplate.update(sql, 100, "lucy");
    }
}
@Service
public class MoneyService {

    @Autowired
    private MoneyDao moneyDao;

    public void accountMoney(){
        moneyDao.reduceMoney();
        moneyDao.addMoney();
    }
}
事务操作
  • 事务添加到JavaEE三层结构里面Service层;
  • 在Spring进行事务管理操作:
    1.有两种方式:编程式事务管理和声明式事务管理;

Spring事务管理API
(1)提供一个接口:PlatformTransactionManager,代表事务管理器,这个接口针对不同的框架提供不同的实现类;

声明式事务管理:

  • 基于注解方式:底层使用AOP;
    1.在Spring配置文件配置事务管理器

    
        
    

2.开启事务注解


    

3.service类上或者方法上添加注解@Transactional
如果把注解添加在类上面,这个类里面所有的方法都添加了事务
如果吧注解添加在方法上面,只是为这个方法添加事务

@Transactional
    public void accountMoney(){
        moneyDao.reduceMoney();
        moneyDao.addMoney();
    }
  • 常见配置参数:
    propagation:事务的传播行为;
    isolation:事务的隔离级别;
    timeout:超时时间,开启事务到提交事务的最长时间,超时自动回滚,Spring默认是-1没有超时时间,设置单位为秒;
    readOnly:只读,默认值为False。当设置为True时,只能查询;
    rollbackFor:设置运行中出现了哪些异常才进行事务回滚;
    noRollbackFor:设置运行中出现了哪些异常不进行回滚;
@Transactional(propagation = Propagation.REQUIRED,
    isolation = Isolation.READ_COMMITTED,
    timeout = 30,
    readOnly = false,
    rollbackFor = NullPointerException.class,
    noRollbackFor = ClassCastException.class)
    public void accountMoney(){
        moneyDao.reduceMoney();
        moneyDao.addMoney();
    }
  • 完全注解声明式事务
    使用配置类替代xml文件
@Configuration // 声明配置类
@ComponentScan(basePackages = "per.xgt")// 开启组件扫描
@EnableTransactionManagement //开启事务
public class txConfig {

    // 数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://119.3.230.11:3306/test?useSSL=false&serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    // 创建JdbcTemplate
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        // 到IOC容器中根据类型找到DataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        // 注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    // 事务管理器对象
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        return transactionManager;
    }

}
  • 基于XML配置文件方式:
    (1)配置事务管理器
    (2)配置通知
    (3)配置切入点与切面

        
        
        
        
    

    
    
        
    

    
    

    
    
        
    

   
    
        
        
            
            
        
    

    
    
        
        
        
        
    
日志

Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2;

Spring整个Log4j2
  • 引入依赖

        
        
            org.apache.logging.log4j
            log4j-api
            2.11.2
        
        
        
            org.apache.logging.log4j
            log4j-core
            2.11.2
        
        
        
            org.apache.logging.log4j
            log4j-slf4j-impl
            2.11.2
            test
        
        
        
            org.slf4j
            slf4j-api
            1.7.30
        
  • 创建log4j2配置文件log4j2.xml(固定名字)



























    
    
    
    
    
    
    


    
        
        
        
        
    
    
    
        
    
    
    
        
        
        
        
            
            
            
        
        
        
    
    
    
        
        
        
        
            
            
            
        
        
        
    
    
    
        
        
        
        
            
            
            
        
        
        
    




    
    
        
    
    
    
    
        
    
    
        
        
        
        
        
    


@Nullable

@Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回、属性值、参数可以为空;

函数式注册对象

Spring5核心容器支持函数式风格GenericApplicationContext

@Test
    public void testGenericApplicationContext(){
        // 创建GenericApplicationContext对象
        GenericApplicationContext context = new GenericApplicationContext();
        // 把里面内容清空,然后进行对象注册
        context.refresh();
        context.registerBean(MoneyService.class,()->new MoneyService());
        // 获取在Spring注册的对象
        MoneyService moneyService = (MoneyService) context.getBean("per.xgt.service.MoneyService");
        System.out.println(moneyService);
    }

还可以

@Test
    public void testGenericApplicationContext2(){
        // 创建GenericApplicationContext对象
        GenericApplicationContext context = new GenericApplicationContext();
        // 把里面内容清空,然后进行对象注册
        context.refresh();
        context.registerBean("moneyService",MoneyService.class,()->new MoneyService());
        // 获取在Spring注册的对象
        MoneyService moneyService = (MoneyService) context.getBean("moneyService");
        System.out.println(moneyService);
    }
JUnit 整合JUnit4
  • 引入Spring相关针对测试依赖

        
            org.springframework
            spring-test
            5.2.6.RELEASE
            test
        
  • 编写测试类
@RunWith(SpringJUnit4ClassRunner.class)// 加载JUnit
@ContextConfiguration("classpath:bean1.xml") // 加载配置文件
public class testJunit4 {

    @Autowired
    private MoneyService moneyService;

    @Test
    public void test1(){
        moneyService.accountMoney();
    }

}
整合JUnit5
  • 引入依赖

        
            org.junit.jupiter
            junit-jupiter-api
            5.3.2
            test
        
  • 创建测试类
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
public class testJUnit5 {

    @Autowired
    private MoneyService moneyService;

    @Test
    public void test1(){
        moneyService.accountMoney();
    }

}
  • 优化,使用组合注解简化
@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class testJUnit5 {

    @Autowired
    private MoneyService moneyService;

    @Test
    public void test1(){
        moneyService.accountMoney();
    }

}
WebFlux SpringWebFlux介绍

使用传统web框架比如SpringMVC,这些基于Servlet容器,WebFlux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的;
异步: 调用者发送请求,不等着对方回应就去做其他的;
同步: 调用者发送请求,如果等着对方回应之后才去做其他事情;

非阻塞: 收到请求后马上给出反馈然后再去做事情;
阻塞: 被调用者收到请求后,做完了请求任务后才给出反馈;
以上都是针对对象不一样

  • 特点
    1)非阻塞式:在有限资源下,提高系统吞吐量和伸缩量,以Reactor为基础实现响应式编程;
    2)函数式编程:Spring5框架基于java8,WebFlux使用函数式编程方式实现路由请求;

  • 与SpringMVC的区别:
    1)两个框架都可以使用注解方式,都运行在Tomcat等容器中;
    2)SpringMVC采用命令式编程,WebFlux采用异步响应式编程

响应式编程(Reactor实现)

响应式编程操作中,Reactor是满足Reactive规范框架

Reactor有以下两个核心类:Mono、Flux这两个类实现接口Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或1个元素

Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值、错误信号、完成信号,其中错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号同时会将错误传递给订阅者;其次,错误信号和完成信号不能共存;如果没有发送任何元素值,直接发送错误或者完成信号,表示是空数据流;如果没有错误信号也没有完成信号,表示无限数据流;

  • 引入依赖


    io.projectreactor
    reactor-core
    3.1.5.RELEASE

  • Mono和Flux
public static void main(String[] args) {
        // just方法直接声明
        Flux.just(1,2,3,4);
        Mono.just(1);

        // 其他方法
        // 数组
        Integer[] arr = {1,2,3,4};
        Flux.fromArray(arr);

        // 集合
        List list = Arrays.asList(arr);
        Flux.fromIterable(list);

        // stream流
        Stream stream = list.stream();
        Flux.fromStream(stream);
    }

调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会发出数据流;

Flux.just(1,2,3,4).subscribe(System.out::print);
        System.out.println();
        Mono.just(1).subscribe(System.out::println);
  • 操作符
    **map:**元素映射为新元素;
    **flatMap:**元素映射为流,把转换之后多个流合并成大的流;
WebFlux执行流程和核心API

SpringWebFlux基于Reactor,默认容器是Netty,Netty是高性能,NIO框架,异步非阻塞的框架;

SpringWebFlux执行过程和SpringMVC相似的:

  • SpringWebFlux核心控制器DispatchHandler,实现接口WebHandler;

  • DispatchHandler负责请求的处理
    1.HandlerMapping根据客户端请求查询请求方法
    2.HandlerAdapter实现具体业务方法
    3.HandlerResultHandler负责响应结果处理

  • SpringMVC方式实现:同步阻塞,基于SpringMVC+Servlet+Tomcat

  • SpringWebFluc方式实现:异步非阻塞,基于SpringWebFlux+Reactor+Netty

SpringWebFlux(基于注解编程模型)
  • 创建SpringBoot工程,引入WebFlux依赖

        
            org.springframework.boot
            spring-boot-starter-webflux
        
  • 在application.properties配置启动的端口号
# 设置启动端口号
server.port=8081
  • 创建包和相关接口与类
    实体类
public class User {

    private String name;
    private String gender;
    private Integer age;

    public User(String name, String gender, Integer age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public User() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                ", gender='" + gender + ''' +
                ", age=" + age +
                '}';
    }
}

创建接口,定义操作方法

public interface UserService {

    // 根据ID查询用户
    Mono findOneById(int id);

    // 查询所有用户
    Flux findAll();

    // 添加用户
    Mono insertOne(Mono user);

}

接口方法实现类

public class UserServiceImpl implements UserService {

    // 创建map集合,存储数据
    private final Map users = new HashMap<>();

    public UserServiceImpl() {
        this.users.put(1,new User("xgt","男",26));
        this.users.put(1,new User("zyk","男",25));
        this.users.put(1,new User("wp","男",25));
        this.users.put(1,new User("NB","男",1000));
    }

    // 查询一个元素
    @Override
    public Mono findOneById(int id) {
        return Mono.justOrEmpty(this.users.get(id));
    }

    // 查询多个元素
    @Override
    public Flux findAll() {
        return Flux.fromIterable(this.users.values());
    }

    // 添加用户
    @Override
    public Mono insertOne(Mono userMono) {
        return userMono.doOnNext(person -> {
            // 向map集合里面方值
            int id = users.size() + 1;
            users.put(id,person);
        }).thenEmpty(Mono.empty());
    }
}

创建controller

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    // id查询
    @GetMapping("/user/findOneById/{id}")
    public Mono findOneById(@PathVariable int id){
        return userService.findOneById(id);
    }

    // 查询所有
    @GetMapping("/user/findAll")
    public Flux findAll(){
        return userService.findAll();
    }

    // 添加
    @PostMapping("/user/insertOne")
    public Mono insertOne(@RequestBody User user){
        Mono userMono = Mono.just(user);
        return userService.insertOne(userMono);
    }

}
SpringWebFlux(基于函数式编程模型)

实现函数式编程,两个接口:
1.RouterFunction:实现路由功能,将请求转发给对应的Handler;
2.HandlerFunction:处理请求,并且响应函数,执行请求的方法;

  • 在使用函数式编程模型操作的时候,需要自己初始化服务器;
  • SpringWebFlux请求和响应不再是ServletRequest和ServletResponse;

代码实现

  • 创建Handler(具体实现方法)
public class UserHandler {

    public final UserService userService;

    public UserHandler(UserService userService){
        this.userService = userService;
    }

    public Mono getUserById(ServerRequest request){
        // 获取ID值
        int id = Integer.valueOf(request.pathVariable("id"));
        // 空值处理
        Mono notFound = ServerResponse.notFound().build();
        // 调用Service的方法得到数据
        Mono userMono = this.userService.findOneById(id);
        // 将userMono进行转换
        // 使用Reactor操作符flatMap
        return userMono.flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(person)).switchIfEmpty(notFound);
    }

    public Mono getAll(ServerRequest request){
        // 调用service得到结果
        Flux users = this.userService.findAll();
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
    }

    public Mono saveUser(ServerRequest request){
        // 得到User对象
        Mono userMono = request.bodyToMono(User.class);
        return ServerResponse.ok().build(this.userService.insertOne(userMono));
    }

}
  • 编写Router、创建服务器、完成适配
public class UserServer {

    public static void main(String[] args) throws Exception {
        UserServer server = new UserServer();
        server.createReactorServer();
        System.out.println("回车退出:");
        System.in.read();
    }

    // 创建Router路由
    public RouterFunction routingFunction(){
        UserService userService = new UserServiceImpl();
        UserHandler userHandler = new UserHandler(userService);

        return RouterFunctions.route(GET("/users/{id}").and(accept(APPLICATION_JSON)), userHandler::getUserById).andRoute(GET("/users").and(accept(APPLICATION_JSON)),userHandler::getAll);
    }


    // 创建服务器完成适配
    public void createReactorServer(){
        // 路由和handler适配
        RouterFunction router = routingFunction();
        HttpHandler httpHandler = toHttpHandler(router);
        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

        // 创建服务器
        HttpServer httpServer = HttpServer.create();
        httpServer.handle(adapter).bindNow();
    }

}

执行main方法,浏览器访问即可;

  • 使用WebClient调用
public static void main(String[] args) {
        WebClient webClient = WebClient.create("http://127.0.0.1:63308");

        // 根据id查询
        String id = "1";
        User userResult = webClient.get().uri("/users/{id}",id)
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class)
                .block();
        System.out.println(userResult.getName());


        // 查询所有
        Flux results = webClient.get().uri("/users")
                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User.class);

        results.map(stu -> stu.getName())
                .buffer().doOnNext(System.out::println).blockFirst();

    }
Spring注解驱动 注册组件

@Configuration 告诉Spring这是一个配置类
@Bean(value = “person”) 给容器注册一个Bean,类型为返回值类型,id默认是方法名作为id; value属性可以指定Bean的id值

// 配置类==配置文件
@Configuration// 告诉Spring这是一个配置类
public class MyConfig {

    // 给容器注册一个Bean,类型为返回值类型,id默认是方法名作为id;
    @Bean(value = "person") // value属性就是指定Bean的id值
    public Person getPerson(){
        return new Person(20, "xgt");
    }

}
自动扫描组件

在配置类上使用此注解:
@ComponentScan(basePackages = “per.xgt”):basePackages指定要扫描的包

指定扫描规则

按照规则指定排除哪些组件excludeFilters = Filter[]
按照规则指定包含哪些组件includeFilters = Filter[],配之前需要关闭扫描

@ComponentScan(basePackages = "per.xgt",useDefaultFilters = false,excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
}//按照规则指定排除哪些组件excludeFilters = Filter[]
,includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
}//按照规则指定包含哪些组件includeFilters = Filter[],陪之前需要关闭扫描的默认规则
)

完整配置

// 配置类==配置文件
@Configuration// 告诉Spring这是一个配置类
@ComponentScan(basePackages = "per.xgt",useDefaultFilters = false,excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
}//按照规则指定排除哪些组件excludeFilters = Filter[]
,includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
}//按照规则指定包含哪些组件includeFilters = Filter[],陪之前需要关闭扫描的默认规则
)
public class MyConfig {
    // 给容器注册一个Bean,类型为返回值类型,id默认是方法名作为id;
    @Bean(value = "person") // value属性就是指定Bean的id值
    public Person getPerson(){
        return new Person(20, "xgt");
    }
}
指定过滤规则typeFilter

ASSIGNABLE_TYPE:指定类型,包括他的子类和实现类

@ComponentScan.Filter(type = 
FilterType.ASSIGNABLE_TYPE,classes = BookService.class)

FilterType.REGEX:正则表达式
ilterType.CUSTOM:自定义规则

@ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)
public class MyTypeFilter implements TypeFilter {
    
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取当前类的注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当前正在扫描的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当前类的资源信息
        Resource resource = metadataReader.getResource();


        String className = classMetadata.getClassName();
        System.out.println("---->"+className);

        if (className.contains("per"))return true;

        return false;
    }
}
组件作用域
@Configuration
public class MyConfig2 {

    
    @Scope("prototype")
    @Bean(value = "person")
    public Person person(){
        return new Person(22, "道士");
    }

}
bean懒加载

单例bean,默认在容器启动的时候创建对象;
懒加载:容器启动不创建对象,在第一次获取bean的时候再创建对象且再进行初始化;

@Lazy
    @Bean(value = "person")
    public Person person(){
        return new Person(22, "道士");
    }
按照条件注册bean

@Conditional:按照一定条件进行判断,满足条件给容器中注册bean

@Conditional(MyConditional.class)
    @Bean("p1")
    public Person person01(){
        return new Person(11, "person01");
    }
public class MyConditional implements Condition {

    
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        // 获取IOC的bean工厂
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        // 获取类加载器
        ClassLoader classLoader = conditionContext.getClassLoader();
        // 获取环境变量值
        Environment environment = conditionContext.getEnvironment();
        // 获取到bean定义的注册类
        BeanDefinitionRegistry registry = conditionContext.getRegistry();

        String property = environment.getProperty("os.name");
        if (property.contains("Lin")) return true;
        return false;
    }
}
@Import导入组件
  • 在配置类上使用注解快速导入组件
@Import(Color.class)// id默认是组件的全类名
  • @ImportSelector导入组件
    返回需要导入的组件的全类名数组
@Import({Color.class,MyImportSelector.class})// id默认是组件的全类名
public class MyImportSelector implements ImportSelector {

    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"per.xgt.bean.Blue"};
    }
}
  • 使用ImportBeanDefinitionRegistrar
@Import({Color.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        boolean red = registry.containsBeanDefinition("per.xgt.bean.Blue");
        boolean blue = registry.containsBeanDefinition("per.xgt.bean.Color");
        if (red && blue){
            // 指定Bean定义信息
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Yellow.class);
            // 注册一个Bean,指定Bean名
            registry.registerBeanDefinition("redBean",beanDefinition);
        }
    }
}
FactoryBean注册组件
@Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
public class ColorFactoryBean implements FactoryBean {
    // 返回一个对象,会添加到容器中
    // 工厂Bean获取的事调用getObject创建的对象
    @Override
    public Color getObject() throws Exception {
        return new Color();
    }

    @Override
    public Class getObjectType() {
        return Color.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
Bean生命周期

生命周期:Bean创建–初始化–销毁的过程

bean指定初始化和销毁方法
  • @Bean注解指定
    **注意:**单实例Bean在容器关闭的时候执行销毁方法,多实例Bean容器不会调用销毁方法;,
public class Car {

    public Car() {
        System.out.println("构造器...");
    }

    public void init(){
        System.out.println("init...");
    }

    public void destroy(){
        System.out.println("destroy...");
    }
}
@Bean(initMethod = "init",destroyMethod = "destroy")
    public Car car(){
        return new Car();
    }
  • 实现InitializingBean和DisposableBean接口
@Component
public class Cat implements InitializingBean, DisposableBean {

    public Cat() {
        System.out.println("构造器...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("销毁...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始...");
    }
}
  • 使用PostConstruct和PreDestroy注解
    PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化
    PreDestroy:在容器销毁Bean前,进行清理工作
@Component
public class Dog {

    public Dog() {
        System.out.println("dog...");
    }

    @PostConstruct
    public void init(){
        System.out.println("init...");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("destroy...");
    }

}
  • Bean的后置处理器BeanPostProcessor
    在bean初始化前后进行处理
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
        System.out.println("postProcessBeforeInitialization..." + o + "-->" + s);
        return o;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        System.out.println("postProcessAfterInitialization..." + o + "-->" + s);
        return o;
    }
}
属性赋值 @Value
@Value("#{50-8}")
    private Integer age;

    @Value("xgt")
    private String name;
@PropertySource加载外部配置文件
  • 加载properties文件
    在配置类上加上注解:@PropertySource将k/v数据读取到环境变量中
@PropertySource("classpath:/person.properties")
@Value("${person.name}")
    private String boomBoxName;
自动装配

Spring利用依赖注入DI,完成对IOC容器中各个组件的依赖关系赋值;

@Autowired

默认先按照类型在容器中找对应的组件,找到就赋值;如果找到相同类型的组件,再将属性的名称作为组件的id取容器中查找;自动装配一定要将属性赋值好,没有就报错;
required属性可以设置是否必须赋值;

@Autowired
    private BookService bookService;
    @Autowired(required = false)
    private BookDao bookDao;
@Qualifier指定要装配的组件id,而不是属性名
@Qualifier("bookDaoImpl")
    @Autowired
    private BookDao bookDao;
@Primary

让Spring进行自动装配的时候,默认使用首选的Bean,也可以继续使用@Qualifier指定需要装配哪个Bean;

@Primary
    @Bean
    public BookService bookService(){
        return new BookServiceImpl();
    }
@Resource

可以和@Autowired一样实现自动装配,默认是按照组件名称进行装配的;
name属性可以注入指定名称的组件

@Inject

和@Autowired功能一样

  • 导入依赖

            javax.inject
            javax.inject
            1
        
@Autowired标注位置 方法上:
@Autowired
    public void setCar(Car car) {
        this.car = car;
    }

Spring容器创建对象的时候,就会调用方法,完成赋值,方法使用的参数,自定义类型的值,容IOC容器中进行获取;

还可以@Bean+方法参数的方式,参数从容器中获取;默认不写Autowired

构造器上:

默认加载IOC容器的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值,如果组件足有一个有参构造器,这个有参构造器的@Autowired可以省略,还是 可以自动从容器中获取;

Aware注入Spring底层组件

自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,XXX),自定义组件实现XXXAware,在创建对象的时候,会调用接口规范的方法注入相关组件;

@Component
public class Red implements
        ApplicationContextAware,
        BeanNameAware,
        EmbeddedValueResolverAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("传入的IOC:"+applicationContext);
        applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("当前bean的名字:" + s);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        String resolveStringValue = stringValueResolver.resolveStringValue("你好${os.name} 我是#{20*18}");
        System.out.println(resolveStringValue);
    }
}
@Profile

Spring为我们提供的可以根据当前环境,动态激活和切换一系列组件的功能
指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境都能注册此组件;
加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境;也可以写在配置类上,只有环境激活相关配置才会生效

  • 多个DataSourceBean
@Profile("test")
    @Bean("testD")
    public DataSource dataSourceTest() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setJdbcUrl("jdbc:mysql://xxx/test");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }
    @Profile("dev")
    @Bean("devD")
    public DataSource dataSourceDev() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setJdbcUrl("jdbc:mysql://xxx/test");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }
    @Profile("prod")
    @Bean("prodD")
    public DataSource dataSourceProd() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setJdbcUrl("jdbc:mysql://xxx/test");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }
  • 启动设置VM参数 -Dspring.profiles.active=xx 使用xx环境

  • 代码方式:

@Test
    public void test2(){

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfigProfile.class);

        // 创建一个applicationContext
        // 设置需要激活的环境
        context.getEnvironment().setActiveProfiles("dev");
        // 注册主配置类
        context.register(MyConfigProfile.class);
        // 启动刷新容器
        //context.refresh();

        String[] names = context.getBeanNamesForType(DataSource.class);
        for (String name : names) {
            System.out.println(name);
        }
        context.close();
    }
SpringAnnotation AOP

AOP:在程序运行期间动态地将某段代码切入到指定方法指定位置进行运行的编程方法;

代码示例:
  • 导入依赖

        
        
            org.springframework
            spring-context
            4.3.12.RELEASE
        
        
        
            org.springframework
            spring-aspects
            4.3.12.RELEASE
        
    
  • 业务逻辑类
public class MathCalculator {

    public int div(int i,int j){
        return i/j;
    }

}
  • 日志切面类
public class LogAspect {

    public void logStart(){
        System.out.println("方法运行...参数是:{}");
    }

    public void logEnd(){
        System.out.println("方法结束...");
    }

    public void logReturn(){
        System.out.println("正常返回...返回结果:{}");
    }

    public void logException(){
        System.out.println("方法异常...异常信息:{}");
    }

}
  • 通知方法
    前置通知@Before:在目标方法之前运行;
    后置通知@After:在目标运行结束之后运行,无论方法正常结束还是异常结束;
    返回通知@AfterReturning:在目标方法正常返回之后运行;
    异常通知@AfterThrowing:在目标方法出现异常之后运行;
    环绕通知@Around:动态代理,手动推进目标方法运行;

  • 给切面类的目标方法标注对应通知注解

public class LogAspect {

    // 抽取公共的切入点表达式
    // 本类引用直接引用方法
    // 其他的切面引用,需要引入全名
    @Pointcut(value = "execution(public int aop.MathCalculator.*(..))")
    public void pointCut(){};

    // 在目标方法之前切入,切入点表达式指定在哪个方法切入;
    @Before(value = "execution(public int aop.MathCalculator.*(..))")
    public void logStart(){
        System.out.println("方法运行...参数是:{}");
    }

    //
    @After("pointCut()")
    public void logEnd(){
        System.out.println("方法结束...");
    }

    @AfterReturning("pointCut()")
    public void logReturn(){
        System.out.println("正常返回...返回结果:{}");
    }

    @AfterThrowing("pointCut()")
    public void logException(){
        System.out.println("方法异常...异常信息:{}");
    }

}
  • 将切面类和业务逻辑类,都加入到容器中
@Configuration
public class MyConfigAOP {
    
    @Bean
    public MathCalculator calculator(){
        return new MathCalculator();
    }
    
    @Bean
    public LogAspect logAspect(){
        return new LogAspect();
    } 
    
}
  • 告诉哪个类是切面类(给切面类加注解)
@Aspect //告诉spring哪个类是切面类
public class LogAspect {
}
  • 开启基于注解的AOP模式
@EnableAspectJAutoProxy
@Configuration
public class MyConfigAOP {}
  • 获取相关参数
    JoinPoint一定要出现在参数表的第一位
@Aspect //告诉spring哪个类是切面类
public class LogAspect {

    // 抽取公共的切入点表达式
    // 本类引用直接引用方法
    // 其他的切面引用,需要引入全名
    @Pointcut(value = "execution(public int aop.MathCalculator.*(..))")
    public void pointCut(){};

    // 在目标方法之前切入,切入点表达式指定在哪个方法切入;
    @Before(value = "execution(public int aop.MathCalculator.*(..))")
    public void logStart(JoinPoint joinPoint){
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        // 获取参数列表
        Object[] args = joinPoint.getArgs();
        System.out.println("@Before...方法名:"+ name +"...参数是:{" + Arrays.asList(args) + "}");
    }

    //
    @After(value = "pointCut()")
    public void logEnd(JoinPoint joinPoint){

        System.out.println("@After...");
    }

    @AfterReturning(value = "pointCut()",returning = "result")
    public void logReturn(Object result){
        System.out.println("@AfterReturning...返回结果:{"+result+"}");
    }

    @AfterThrowing(value = "pointCut()",throwing = "exception")
    public void logException(Exception exception){
        System.out.println(" @AfterThrowing...异常信息:{"+exception+"}");
    }

}
AOP原理
 
声明式事务 
环境搭建 
  • 依赖

        
        
            org.springframework
            spring-context
            4.3.12.RELEASE
        
        
        
            org.springframework
            spring-aspects
            4.3.12.RELEASE
        
        
            junit
            junit
            RELEASE
            test
        
        
        
            c3p0
            c3p0
            0.9.1.2
        
        
        
            mysql
            mysql-connector-java
            5.1.44
        
        
            org.springframework
            spring-jdbc
            4.3.12.RELEASE
        
    
  • 配置数据源,JDBCTemplate
@ComponentScan("tx")
@Configuration
public class MyConfigTx {

    @Bean
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return jdbcTemplate;
    }

}
  • 开启基于注解的事务管理功能
@EnableTransactionManagement
  • 注册配置事务管理器
@Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1028809.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号