Spring

目录

一、程序入门

(一)、入门案例

1、创建父工程

2、创建子工程

3、在子工程的pom文件中引入spring核心依赖

4、在子工程中创建实体类

5、在resources中添加配置文件(.xml)

6、编写测试类

7、spring中用反射创建实体类

(二)、启用Log4j2日志框架

1、引入依赖

2、在类的根路径下提供log4j2.xml配置文件

3、手动写日志

二、容器IoC

(一)、基于xml文件进行bean管理

1、xml文件中创建对象

2、获取对象

3、依赖注入

(1)、基于set方法

(2)、基于构造器完成

(3)、特殊值处理 

1)、空值处理(null)

2)、xml实体

3)、CDATA节

  (4)对象类型属性注入

1)、引用外部bean

2)、内部bean

3)、级联属性赋值

(5)、数组类型注入 

 (6)、集合类型注入 

1)、List

 2)、Map

3)、引用集合类型的bean

(7)、p命名空间注入

 (8)、引入外部属性文件

1)、引入相关依赖

2)、在resources下写相关的配置文件

3)、在xml文件中进行依赖注入

4、bean的作用域 

 5、bean的生命周期

(1)、基本生命周期过程

(2)、配置bean

(3)、测试

6、FactoryBean

7、基于xml自动装配(autowire)

(1)、创建实体类

(2)、配置bean

(3)、测试

(二)、基于注解管理bean

1、开启组件扫描

(1)、最基本扫描方式

(2)、排除指定组件

(3) 、只扫描指定组件

2、使用注解定义 Bean

3、@Autowired注入(默认按类型注入)

(1)、属性注入

(2)、set注入

(3)、构造方法注入

(4)、形参上注入

(5)、只有一个构造函数,无注解

(6)、@Autowired注解和@Qualifier注解联合

4、@Resource注入(默认根据名称自动装配)

(1)、根据name注入

(2)、name未知注

(3)、其他情况

@Resource注解:默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个

(三)、Spring全注解开发

1、配置类

2、测试类

三、原理-手写IoC

(一)、回顾java反射

(二)、实现spring的IoC

1、准备测试用的bean

2、定义注解

3、编写逻辑

4、测试

四、面向切面: AOP

(1)、动态代理

(二)、基于注解的AOP

1、引入依赖

2、准备被代理的目标资源

3、在Spring的配置文件中配置

4、创建切面类

5、切入点表达式

6、重用切入点表达式

7、切面的优先级

(三)、基于xml的AOP

1、把切面类中的注解删除

2、配置文件

五、单元测试: Junit

(一)、整合Junit5

1、引入依赖

2、添加配置文件

 3、测试

(二)、整合Junit4

1、添加依赖

2、测试

六、声明式事务

(一)、基于注解的声明式事务

1、配置文件

2、添加注解@Transactional

3、事务属性

(1)、只读

(2)、超时

(3)、回滚策略

(4)、隔离级别

(5)、传播行为

4、全注解配置事务

(二)、基于XML的声明式事务

七、资源操作:Resources

(一)、Resource的实现类

1、UrlResource访问网络资源

2、ClassPathResource 访问类路径下资源

3、FileSystemResource 访问文件系统资源

(二)、ResourceLoader 接口

(三)、ResourceLoaderAware 接口

(四)、使用Resource 作为属性

(五)、应用程序上下文和资源路径

八、数据校验

(一)、通过Validator接口实现

1、引入依赖

2、创建实体类

3、创建类实现Validator接口,实现接口方法指定校验规则

4、使用上述Validator进行测试

(二)、Bean Validation注解实现

1、创建配置类

2、创建实体类,使用注解定义校验规则

3、使用两种不同的校验器实现

(1)使用jakarta.validation.Validator校验

(2)使用org.springframework.validation.Validator校验

4、 测试

(三)、基于方法实现校验

1、 创建配置类,配置MethodValidationPostProcessor

2、 创建实体类,使用注解设置校验规则

3、 定义Service类,通过注解操作对象

4、测试

(四)、实现自定义校验

1、自定义校验注解

2、编写真正的校验类


一、程序入门

(一)、入门案例

1、创建父工程

2、创建子工程

3、在子工程的pom文件中引入spring核心依赖

 
        
        
        
            org.springframework
            spring-context
            6.0.2
        

        
        
            org.junit.jupiter
            junit-jupiter-api
            5.3.1
        
    

4、在子工程中创建实体类

public class User {

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

5、在resources中添加配置文件(.xml)

bean.xml




    
    

6、编写测试类

@Test
public void testUserObject(){
    //加载spring配置文件,对象创建
    ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("bean.xml");

    //获取创建的对象
    User user = (User)context.getBean("user");
    System.out.println(user);

    //使用对象方法
    user.add();
}

7、spring中用反射创建实体类

@Test
public void testUserObject1() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //获取class对象
    Class clazz = Class.forName("com.atguigu.spring.User");

    //调用方法创建对象
    //Object o = clazz.newInstance(); 过时了
    User user = (User) clazz.getDeclaredConstructor().newInstance();

    user.add();
}

(二)、启用Log4j2日志框架

1、引入依赖



    org.apache.logging.log4j
    log4j-core
    2.19.0


    org.apache.logging.log4j
    log4j-slf4j2-impl
    2.19.0

2、在类的根路径下提供log4j2.xml配置文件

文件名固定为:log4j2.xml,文件必须放到类根路径下。



    
        
        
            
            
            
        
    

    
        
        
            
            
        

        
        
            
        

        
        
            
            
            
            
        
    

3、手动写日志

public class TestUser {

    private Logger logger = LoggerFactory.getLogger(TestUser.class);


    @Test
    public void testUserObject(){
        //加载spring配置文件,对象创建
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");

        //获取创建的对象
        User user = (User)context.getBean("user");
        System.out.println(user);

        //使用对象方法
        user.add();

        //手动写日志
        logger.info("### 执行调用成功了...");
    }
}

二、容器IoC

(一)、基于xml文件进行bean管理

1、xml文件中创建对象




    

2、获取对象

package com.atguigu.spring.iocxml;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testUser {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");

        //1、根据id获取bean
        User user1 = (User)context.getBean("user");
        System.out.println(user1);

        //2、根据类型获取bean(要求IoC容器中该实例的bean只能有一个)
        User user2 = (User)context.getBean(User.class);
        System.out.println(user2);

        //根据id和类型获取bean
        User user3 = (User)context.getBean("user",User.class);
        System.out.println(user2);

    }
}

3、依赖注入

类中有属性,创建对象过程中,像属性设置值 

(1)、基于set方法

新建book类,添加set方法

package com.atguigu.spring.iocxml.di;

public class Book {
    private String bname;
    private String author;


    //生成set方法
    public void setBname(String bname) {
        this.bname = bname;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bname='" + bname + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}

bean.xml文件中进行配置




    
    
        
        
    

 测试

package com.atguigu.spring.iocxml.di;

import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testBook {

    @Test
    public void testSetter(){
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("bean-di.xml");

        //1、根据id获取bean
        Book book = (Book)context.getBean("book");
        System.out.println(book);
    }
}
(2)、基于构造器完成

类中添加构造方法

package com.atguigu.spring.iocxml.di;

public class Book {
    private String bname;
    private String author;

    //有参无参构造
    public Book(){
        System.out.println("无参构造执行了...");
    }

    public Book(String bname, String author) {
        this.bname = bname;
        this.author = author;
        System.out.println("有参构造执行了...");
    }

    @Override
    public String toString() {
        return "Book{" +
                "bname='" + bname + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}

配置文件中设置





    
        
        
    

测试

package com.atguigu.spring.iocxml.di;

import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testBook {


    @Test
    public void testConstructor(){
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("bean-di.xml");

        //1、根据id获取bean
        Book book = (Book)context.getBean("bookCon",Book.class);
        System.out.println(book);
    }
}
(3)、特殊值处理 
1)、空值处理(null)
    
        
        
            
        
    
2)、xml实体


3)、CDATA节

    
    
    
    
    
  (4)对象类型属性注入
1)、引用外部bean

新建类

package com.atguigu.spring.iocxml.ditest;

public class Dept {
    private String dname;

    public void info(){
        System.out.println(dname);
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }
}
package com.atguigu.spring.iocxml.ditest;

public class Emp {
    private String ename;
    private Integer age;

    //员工属于哪个部门
    private Dept dept;

    public void work(){
        System.out.println(ename + " emp work... " + age);
        dept.info();
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Integer getAge() {
        return age;
    }

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

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}



    
        
    

    
        
        
        
        
    
2)、内部bean


    
    
        
        
        
        
            
                
            
        
    
    
3)、级联属性赋值



    
        
    
    
    
        
        
        
        
        
    

(5)、数组类型注入 
package com.atguigu.spring.iocxml.ditest;

public class Emp {
    private String ename;
    private Integer age;
    
    //爱好
    private String[] loves;

    //员工属于哪个部门
    private Dept dept;

    public void work(){
        System.out.println(ename + " emp work... " + age);
        dept.info();
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Integer getAge() {
        return age;
    }

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

    public String[] getLoves() {
        return loves;
    }

    public void setLoves(String[] loves) {
        this.loves = loves;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }
}



    
        
    

    
        
        
        
        
        
            
                吃饭
                睡觉
                写代码
            
        
    

 (6)、集合类型注入 
1)、List
package com.atguigu.spring.iocxml.ditest;

import java.util.List;

public class Dept {
    private String dname;

    //部门中有哪些部门
    private List empList;

    public void info(){
        System.out.println(dname);
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public List getEmpList() {
        return empList;
    }

    public void setEmpList(List empList) {
        this.empList = empList;
    }
}
    
        
        
            
                
                
            
        
    

 2)、Map

    
    



    
    



    
        
            
                
                    10010
                
                
            
            
                
                    10086
                
                
            
        
    
3)、引用集合类型的bean



    
    
    



    
        
            10010
        
        
    
    
        
            10086
        
        
    


    
    
    


    
    
    
    
    
    
    
        
            抽烟
            喝酒
            烫头
        
    
    

注意:使用util:list、util:map标签必须引入相应的命名空间


(7)、p命名空间注入

 (8)、引入外部属性文件

以数据库为例

1)、引入相关依赖
 

    mysql
    mysql-connector-java
    8.0.30




    com.alibaba
    druid
    1.2.15
2)、在resources下写相关的配置文件

jdbc.properties

jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
3)、在xml文件中进行依赖注入



    
    

    
    
        
        
        
        
    

4、bean的作用域 



@Test
public void testBeanScope(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("spring-scope.xml");
    User user1 = ac.getBean(User.class);
    User user2 = ac.getBean(User.class);
    //如果scope="prototype",返回false。 如果scope="singleton",返回true。
    System.out.println(user1==user2);
}

 5、bean的生命周期

(1)、基本生命周期过程
  • bean对象创建(调用无参构造器)

  • 给bean对象设置属性

  • bean的后置处理器(初始化之前)

  • bean对象初始化(需在配置bean时指定初始化方法)

  • bean的后置处理器(初始化之后)

  • bean对象就绪可以使用

  • bean对象销毁(需在配置bean时指定销毁方法)

  • IOC容器关闭

(2)、配置bean



    
    
    
    
(3)、测试
@Test
public void testLife(){
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
    User bean = ac.getBean(User.class);
    System.out.println("生命周期:4、通过IOC容器获取bean并使用");
    ac.close(); //调用销毁方法
}

6、FactoryBean

利用泛型特点,虽然配置时不时User类,但生成的bean时User类型的

package com.atguigu.spring6.bean;
public class UserFactoryBean implements FactoryBean {
    @Override
    public User getObject() throws Exception {
        return new User();
    }

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

7、基于xml自动装配(autowire)

(1)、创建实体类

对应的实体类中要自动装配的属性要有setter方法

Controller层

package com.atguigu.spring6.autowire.controller
public class UserController {

    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void saveUser(){
        userService.saveUser();
    }

}

Service层

package com.atguigu.spring6.autowire.service
public interface UserService {

    void saveUser();

}
package com.atguigu.spring6.autowire.service.impl
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void saveUser() {
        userDao.saveUser();
    }

}

Dao层

package com.atguigu.spring6.autowire.dao
public interface UserDao {

    void saveUser();

}
package com.atguigu.spring6.autowire.dao.impl
public class UserDaoImpl implements UserDao {

    @Override
    public void saveUser() {
        System.out.println("保存成功");
    }

}
(2)、配置bean

autowire="byType" : 按类型自动装配

autowire="byName" : 按名称自动装配(对象名与id相同)





(3)、测试
@Test
public void testAutoWireByXML(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");
    UserController userController = ac.getBean(UserController.class);
    userController.saveUser();
}

(二)、基于注解管理bean

1、开启组件扫描



    
    
(1)、最基本扫描方式
(2)、排除指定组件

    
    
    
        
(3) 、只扫描指定组件

    
    
    
    
    
	

2、使用注解定义 Bean

​​​​

Spring_第1张图片

3、@Autowired注入(默认按类型注入)

(1)、属性注入
package com.atguigu.spring6.controller;

import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    public void out() {
        userService.out();
        System.out.println("Controller层执行结束。");
    }

}
(2)、set注入

效果与第一种相似,但能在set方法中写一些其他的逻辑

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}
(3)、构造方法注入
package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}
(4)、形参上注入

构造方法的形参上架@Autowired

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public UserServiceImpl(@Autowired UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}
(5)、只有一个构造函数,无注解

当有参数的构造方法只有一个时,@Autowired注解可以省略。

说明:有多个构造方法时呢?大家可以测试(再添加一个无参构造函数),测试报错

新版6.0.7本可以一个有参一个无参

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}
(6)、@Autowired注解和@Qualifier注解联合

@Qualifier("") 根据名称注入

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    @Qualifier("userDaoImpl") // 指定bean的名字
    private UserDao userDao;

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

4、@Resource注入(默认根据名称自动装配)

@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。


    jakarta.annotation
    jakarta.annotation-api
    2.1.1
(1)、根据name注入
package com.atguigu.spring6.dao.impl;

import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository("myUserDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}
package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Resource(name = "myUserDao")
    private UserDao myUserDao;

    @Override
    public void out() {
        myUserDao.print();
        System.out.println("Service层执行结束");
    }
}
(2)、name未知注
package com.atguigu.spring6.dao.impl;

import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository("myUserDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}
package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao myUserDao;

    @Override
    public void out() {
        myUserDao.print();
        System.out.println("Service层执行结束");
    }
}
(3)、其他情况
@Resource注解:默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个

(三)、Spring全注解开发

全注解开发就是不再使用spring配置文件(.xml)了,写一个配置类来代替配置文件。

1、配置类

@Configuration  //配置类的注解
@ComponentScan("com.atguigu.spring6") //设置组件扫描范围

package com.atguigu.spring6.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
//@ComponentScan({"com.atguigu.spring6.controller", "com.atguigu.spring6.service","com.atguigu.spring6.dao"})
@ComponentScan("com.atguigu.spring6") //设置组件扫描范围
public class Spring6Config {
}

2、测试类

@Test
public void testAllAnnotation(){
    ApplicationContext context = new 
                        AnnotationConfigApplicationContext(Spring6Config.class);
    UserController userController = context.getBean("userController", UserController.class);
    userController.out();
    logger.info("执行成功");
}

三、原理-手写IoC

Spring框架的IOC是基于Java反射机制实现的

(一)、回顾java反射

创建类

package com.atguigu.reflect;

public class Car {

    private String name;
    private int age;
    private String color;

    public Car(){}

    public Car(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    private void run(){
        System.out.println("私有方法....");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}
package com.atguigu.reflect;

import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestCar {

    //1、获取Class对象多种方式
    @Test
    public void test01() throws Exception {
        //1 类名.class
        Class clazz1 = Car.class;

        //2 对象.getClass()
        Class clazz2 = new Car().getClass();

        //3 Class.forName("全路径")
        Class clazz3 = Class.forName("com.atguigu.reflect.Car");

        //实例化
        Car car = (Car)clazz3.getConstructor().newInstance();
        System.out.println(car);
    }

    //2、获取构造方法
    @Test
    public void test02() throws Exception {
        Class clazz = Car.class;
        //获取所有构造
        // getConstructors()获取所有public的构造方法
//        Constructor[] constructors = clazz.getConstructors();
        // getDeclaredConstructors()获取所有的构造方法public  private
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor c:constructors) {
            System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount());
        }

        //指定有参数构造创建对象
        //1 构造public
//        Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
//        Car car1 = (Car)c1.newInstance("夏利", 10, "红色");
//        System.out.println(car1);

        //2 构造private
        Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        c2.setAccessible(true);
        Car car2 = (Car)c2.newInstance("捷达", 15, "白色");
        System.out.println(car2);
    }

    //3、获取属性
    @Test
    public void test03() throws Exception {
        Class clazz = Car.class;
        Car car = (Car)clazz.getDeclaredConstructor().newInstance();
        //获取所有public属性
        //Field[] fields = clazz.getFields();
        //获取所有属性(包含私有属性)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field:fields) {
            if(field.getName().equals("name")) {
                //设置允许访问
                field.setAccessible(true);
                field.set(car,"五菱宏光");
                System.out.println(car);
            }
            System.out.println(field.getName());
        }
    }

    //4、获取方法
    @Test
    public void test04() throws Exception {
        Car car = new Car("奔驰",10,"黑色");
        Class clazz = car.getClass();
        //1 public方法
        Method[] methods = clazz.getMethods();
        for (Method m1:methods) {
            //System.out.println(m1.getName());
            //执行方法 toString
            if(m1.getName().equals("toString")) {
                String invoke = (String)m1.invoke(car);
                //System.out.println("toString执行了:"+invoke);
            }
        }

        //2 private方法
        Method[] methodsAll = clazz.getDeclaredMethods();
        for (Method m:methodsAll) {
            //执行方法 run
            if(m.getName().equals("run")) {
                m.setAccessible(true);
                m.invoke(car);
            }
        }
    }
}

(二)、实现spring的IoC

1、准备测试用的bean

package com.atguigu.spring6.test.dao;

public interface UserDao {

    public void print();
}
package com.atguigu.spring6.test.dao.impl;

import com.atguigu.spring.dao.UserDao;

public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}
package com.atguigu.spring6.test.service;

public interface UserService {

    public void out();
}
package com.atguigu.spring.test.service.impl;

import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.service.UserService;

@Bean
public class UserServiceImpl implements UserService {

//    private UserDao userDao;

    @Override
    public void out() {
        //userDao.print();
        System.out.println("Service层执行结束");
    }
}

2、定义注解

@bean

package com.atguigu.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}

@Di

package com.atguigu.anno;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

3、编写逻辑

package com.atguigu.bean;

public interface ApplicationContext {

    Object getBean(Class clazz);
}
package com.atguigu.bean;

import com.atguigu.anno.Bean;
import com.atguigu.anno.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext implements ApplicationContext {

    //创建map集合,放bean对象
    private Map beanFactory = new HashMap<>();

    private static String rootPath;

    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //设置包的扫描规则
    public AnnotationApplicationContext(String basePackage) {
        try {
            //1、把.替换成/
            String packagePath = basePackage.replaceAll("\\.", "\\\\");

            //2、获得包的绝对路径
            Enumeration urls
                    = Thread.currentThread()
                    .getContextClassLoader()
                    .getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(), "utf-8");

                //获取包前面路径部分,字符串截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                //包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //属性注入
        loadDi();
    }

    private void loadBean(File file) throws Exception {
        //1、判断当前是否是文件夹
        if (file.isDirectory()) {
            //2.获取文件夹中的所有文件
            File[] childrenFiles = file.listFiles();

            //3.判断文件夹里是否为空
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }

            //4.文件夹里不为空,遍历文件夹所有内容
            for (File child : childrenFiles) {
                //4.1遍历每个File对象,继续判断,如果还是文件夹,递归
                if (child.isDirectory()) {
                    loadBean(child);
                } else {
                    //4.2 遍历的File是文件
                    //4.3 得到包路径+类名称部分
                    String pathWithClass =
                            child.getAbsolutePath()
                                    .substring(rootPath.length() - 1);

                    //4.4判断当前文件类型是否是 .class
                    if (pathWithClass.endsWith(".class")) {
                        //4.5 如果是.class类型,把路径\替换成.  把.class去掉
                        String allName = pathWithClass.replaceAll("\\\\", ".")
                                .replace(".class", "");

                        //4.6 判断类上面是否有注解@Bean,如果有实例化过程
                        //4.6.1 获取类的Class
                        Class clazz = Class.forName(allName);
                        //4.6.2 判断是不是接口,把非接口类实例化对象放在map中
                        if (!clazz.isInterface()) {
                            //4.6.3 判断类上面是否有注解 @Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if (annotation != null) {
                                //4.6.4 实例化
                                Object instance = clazz.getConstructor().newInstance();

                                //4.7 把实例化对象放入到map集合中
                                //4.7.1 判断当前类是否有接口,让接口class作为map的key
                                if (clazz.getInterfaces().length > 0) {
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                } else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void loadDi() {
        //1 遍历beanFactory的map集合
        Set> entries = beanFactory.entrySet();
        for (Map.Entry entry : entries) {
            //2 获得map集合每个对象的value,每个对象属性获取到
            Object obj = entry.getValue();

            //获取对象class
            Class clazz = obj.getClass();

            //获取对象的属性
            Field[] declaredFields = clazz.getDeclaredFields();

            //3 遍历每个对象属性数组,得到每个属性
            for (Field field : declaredFields) {
                //4 判断属性上是否有@Di注解
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null){
                    //如果是私有属性,设置可以设置值
                    field.setAccessible(true);

                    //5 如果有@Di注解,把对象注入
                    try {
                        field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

}

4、测试

package com.atguigu;

import com.atguigu.bean.AnnotationApplicationContext;
import com.atguigu.bean.ApplicationContext;
import com.atguigu.service.UserService;

public class TestUser {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationApplicationContext("com.atguigu");
        UserService userService = (UserService)context.getBean(UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

四、面向切面: AOP

(1)、动态代理

public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxy(){

        /**
         * newProxyInstance():创建一个代理实例
         * 其中有三个参数:
         * 1、classLoader:加载动态生成的代理类的类加载器
         * 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
         * 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
         */
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class[] interfaces = target.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * proxy:代理对象
                 * method:代理对象需要实现的方法,即其中需要重写的方法
                 * args:method所对应方法的参数
                 */
                Object result = null;
                try {
                    System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
                    result = method.invoke(target, args);
                    System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
                } finally {
                    System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
                }
                return result;
            }
        };

        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

(二)、基于注解的AOP

1、引入依赖

 
    
        org.springframework
        spring-aop
        6.0.2
    
    
    
        org.springframework
        spring-aspects
        6.0.2
    

2、准备被代理的目标资源

package com.atguigu.spring.aop.annoaop;

public interface Calculator {

    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);

}
package com.atguigu.spring.aop.annoaop;

public class CalculatorImpl implements Calculator {

    @Override
    public int add(int i, int j) {
        int result = i + j;
        System.out.println("方法内部 result = " + result);
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        System.out.println("方法内部 result = " + result);
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        System.out.println("方法内部 result = " + result);
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        System.out.println("方法内部 result = " + result);
        return result;
    }
}

3、在Spring的配置文件中配置



    

    
    

    
    

4、创建切面类

package com.atguigu.spring.aop.annoaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//切面类
@Aspect  //表示是一个切面类
@Component  //交给spring的ioc容器管理
public class LogAspect {

    //前置通知
    @Before("execution(public int com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName(); //方法名
        String args = Arrays.toString(joinPoint.getArgs()); //参数
        System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
    }

    //后置通知(在返回之后执行,类似于try-catch-finally 中的 finally)
//    @After("execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
//    @After(value = "com.atguigu.spring.aop.annoaop.LogAspect.pointCut()")
    @After(value = "pointCut()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置通知,方法名:"+methodName);
    }

    //返回通知(在返回之前执行,可以得到目标函数的返回值)
    @AfterReturning(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
    }

    //异常通知(目标方法出现异常时执行)
    @AfterThrowing(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
    }

    //环绕通知
    @Around("execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            //类似前置通知
            System.out.println("环绕通知-->目标对象方法执行之前");
            //目标对象(连接点)方法的执行
            result = joinPoint.proceed();
            //类似返回通知
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {  //类似异常通知
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {  //类似后置通知
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }

    //重用切入点表达式
    @Pointcut(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
    public void pointCut(){}

}

5、切入点表达式

Spring_第2张图片

6、重用切入点表达式

//重用切入点表达式
    @Pointcut(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
    public void pointCut(){}
    //后置通知(在返回之后执行,类似于try-catch-finally 中的 finally)
//    @After("execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
//    @After(value = "com.atguigu.spring.aop.annoaop.LogAspect.pointCut()")
    @After(value = "pointCut()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置通知,方法名:"+methodName);
    }

7、切面的优先级

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面

  • 优先级低的切面:里面

使用@Order注解(在切面类上加)可以控制切面的优先级:

  • @Order(较小的数):优先级高

  • @Order(较大的数):优先级低

  • Spring_第3张图片

(三)、基于xml的AOP

1、把切面类中的注解删除

package com.atguigu.spring.aop.xmlaop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//切面类
@Component  //交给spring的ioc容器管理
public class LogAspect {

    //前置通知
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName(); //方法名
        String args = Arrays.toString(joinPoint.getArgs()); //参数
        System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
    }

    //后置通知
    @After(value = "pointCut()")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置通知,方法名:"+methodName);
    }

    //返回通知
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
    }

    //异常通知
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
    }

    //环绕通知
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            //类似前置通知
            System.out.println("环绕通知-->目标对象方法执行之前");
            //目标对象(连接点)方法的执行
            result = joinPoint.proceed();
            //类似返回通知
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {  //类似异常通知
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {  //类似后置通知
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }

}

2、配置文件




    
    

    
    
        
        
            
            
            
            
            
            
            
            
        
    

五、单元测试: Junit

(一)、整合Junit5

1、引入依赖


    
        org.springframework
        spring-test
        6.0.2
    

    
    
        org.junit.jupiter
        junit-jupiter-api
        5.9.0
    

2、添加配置文件



    

 3、测试

package com.atguigu.spring.junit.junit5;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

//第一种方式
//@ExtendWith(SpringExtension.class)
//@ContextConfiguration("classpath:bean.xml")
//第二种方式
@SpringJUnitConfig(locations = "classpath:bean.xml")
public class SpringTestJunit5 {

    @Autowired
    private User user;

    @Test
    public void testUse(){
        user.run();
    }
}

(二)、整合Junit4

1、添加依赖



    junit
    junit
    4.12

2、测试

import com.atguigu.spring6.bean.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:beans.xml")
public class SpringJUnit4Test {

    @Autowired
    private User user;

    @Test
    public void testUser(){
        System.out.println(user);
    }
}

六、声明式事务

(一)、基于注解的声明式事务

1、配置文件

在spring配置文件中引入tx命名空间


 在Spring的配置文件中添加配置:


    




2、添加注解@Transactional

@Transactional标识在方法上,则只会影响该方法

@Transactional标识的类上,则会影响类中所有的方法

3、事务属性

(1)、只读

只能进行查询操作

@Transactional(readOnly = true)
(2)、超时

默认值为-1永不超时

//超时时间单位秒
@Transactional(timeout = 3)
(3)、回滚策略

声明式事务默认只针对运行时异常回滚,编译时异常不回滚。

可以通过@Transactional中相关属性设置回滚策略

  • rollbackFor属性:需要设置一个Class类型的对象

  • rollbackForClassName属性:需要设置一个字符串类型的全类名

  • noRollbackFor属性:需要设置一个Class类型的对象

  • rollbackFor属性:需要设置一个字符串类型的全类名

@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) {
    //查询图书的价格
    Integer price = bookDao.getPriceByBookId(bookId);
    //更新图书的库存
    bookDao.updateStock(bookId);
    //更新用户的余额
    bookDao.updateBalance(userId, price);
    System.out.println(1/0);
}

java程序中仍然抛出了异常,但是数据库不回滚 

(4)、隔离级别

Spring_第4张图片

@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化
(5)、传播行为

Spring_第5张图片

有A、B两个方法且方法上都有事务注解,A中连续两次调用B ,第一次成功,第二次失败。

@Transactional(propagation = Propagation.REQUIRED),默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行 :

 B上的注解为@Transactional(propagation = Propagation.REQUIRED),因为式A调用B,所以线程上已经有A的事务,所以直接执行A的事务,两次调用都回滚。

@Transactional(propagation = Propagation.REQUIRES_NEW),表示不管当前线程上是否有已经开启的事务,都要开启新事务:

B上的注解为@Transactional(propagation = Propagation.REQUIRED),不管当前线程上是否有已经开启的事务,都要开启新事务,每调用都是在B的事务中执行,所以第一次成功(不回滚),第二失败(回滚)

4、全注解配置事务

package com.atguigu.spring6.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;

@Configuration
@ComponentScan("com.atguigu.spring6")
@EnableTransactionManagement
public class SpringConfig {

    @Bean
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

测试: 

import com.atguigu.spring6.config.SpringConfig;
import com.atguigu.spring6.controller.BookController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

public class TxByAllAnnotationTest {

    @Test
    public void testTxAllAnnotation(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookController accountService = applicationContext.getBean("bookController", BookController.class);
        accountService.buyBook(1, 1);
    }
}

(二)、基于XML的声明式事务

将Spring配置文件中去掉tx:annotation-driven 标签,并添加配置:




    
    

    
    
    
    
         
         
         
         
     

    
    
        
    

    
    
        
    

    
    
        
            
            
            
        
    

    
    
        
        
    
 

七、资源操作:Resources

Spring 的 Resource 接口位于 org.springframework.core.io 中。 旨在成为一个更强大的接口,用于抽象对低级资源的访问。以下显示了Resource接口定义的方法

(一)、Resource的实现类

1、UrlResource访问网络资源

Resource的一个实现类,用来访问网络资源,它支持URL的绝对路径。

http:------该前缀用于访问基于HTTP协议的网络资源。

ftp:------该前缀用于访问基于FTP协议的网络资源

file: ------该前缀用于从文件系统中读取资源

实验:访问基于HTTP协议的网络资源

package com.atguigu.spring6.resources;

import org.springframework.core.io.UrlResource;

public class UrlResourceDemo {

    public static void loadAndReadUrlResource(String path){
        // 创建一个 Resource 对象
        UrlResource url = null;
        try {
            url = new UrlResource(path);
            // 获取资源名
            System.out.println(url.getFilename());
            System.out.println(url.getURI());
            // 获取资源描述
            System.out.println(url.getDescription());
            //获取资源内容
            System.out.println(url.getInputStream().read());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void main(String[] args) {
        //访问网络资源
        loadAndReadUrlResource("http://www.baidu.com");
    }
}

实验二:在项目根路径下创建文件,从文件系统中读取资源

方法不变,修改调用传递路径

public static void main(String[] args) {
    //1 访问网络资源
	//loadAndReadUrlResource("http://www.atguigu.com");
    
    //2 访问文件系统资源
    loadAndReadUrlResource("file:atguigu.txt");
}

2、ClassPathResource 访问类路径下资源

ClassPathResource 用来访问类加载路径下的资源,相对于其他的 Resource 实现类,其主要优势是方便访问类加载路径里的资源,尤其对于 Web 应用,ClassPathResource 可自动搜索位于 classes 下的资源文件,无须使用绝对路径访问。

实验:在类路径下创建文件atguigu.txt,使用ClassPathResource 访问

package com.atguigu.spring6.resources;

import org.springframework.core.io.ClassPathResource;
import java.io.InputStream;

public class ClassPathResourceDemo {

    public static void loadAndReadUrlResource(String path) throws Exception{
        // 创建一个 Resource 对象
        ClassPathResource resource = new ClassPathResource(path);
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        //获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
            System.out.println(new String(b));
        }
    }

    public static void main(String[] args) throws Exception {
        loadAndReadUrlResource("atguigu.txt");
    }
}

3、FileSystemResource 访问文件系统资源

Spring 提供的 FileSystemResource 类用于访问文件系统资源,使用 FileSystemResource 来访问文件系统资源并没有太大的优势,因为 Java 提供的 File 类也可用于访问文件系统资源。

实验:使用FileSystemResource 访问文件系统资源

package com.atguigu.spring6.resources;

import org.springframework.core.io.FileSystemResource;

import java.io.InputStream;

public class FileSystemResourceDemo {

    public static void loadAndReadUrlResource(String path) throws Exception{
        //相对路径
        FileSystemResource resource = new FileSystemResource("atguigu.txt");
        //绝对路径
        //FileSystemResource resource = new FileSystemResource("C:\\atguigu.txt");
        // 获取文件名
        System.out.println("resource.getFileName = " + resource.getFilename());
        // 获取文件描述
        System.out.println("resource.getDescription = "+ resource.getDescription());
        //获取文件内容
        InputStream in = resource.getInputStream();
        byte[] b = new byte[1024];
        while(in.read(b)!=-1) {
            System.out.println(new String(b));
        }
    }

    public static void main(String[] args) throws Exception {
        loadAndReadUrlResource("atguigu.txt");
    }
}

(二)、ResourceLoader 接口

实验一:ClassPathXmlApplicationContext获取Resource实例

package com.atguigu.spring6.resouceloader;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;

public class Demo1 {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext();
//        通过ApplicationContext访问资源
//        ApplicationContext实例获取Resource实例时,
//        默认采用与ApplicationContext相同的资源访问策略
        Resource res = ctx.getResource("atguigu.txt");
        System.out.println(res.getFilename());
    }
}

实验二:FileSystemApplicationContext获取Resource实例

package com.atguigu.spring6.resouceloader;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;

public class Demo2 {

    public static void main(String[] args) {
        ApplicationContext ctx = new FileSystemXmlApplicationContext();
        Resource res = ctx.getResource("atguigu.txt");
        System.out.println(res.getFilename());
    }
}

Spring将采用和ApplicationContext相同的策略来访问资源。也就是说,如果ApplicationContext是FileSystemXmlApplicationContext,res就是FileSystemResource实例;如果ApplicationContext是ClassPathXmlApplicationContext,res就是ClassPathResource实例

当Spring应用需要进行资源访问时,实际上并不需要直接使用Resource实现类,而是调用ResourceLoader实例的getResource()方法来获得资源,ReosurceLoader将会负责选择Reosurce实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来

另外,使用ApplicationContext访问资源时,可通过不同前缀指定强制使用指定的ClassPathResource、FileSystemResource等实现类

Resource res = ctx.getResource("calsspath:bean.xml");
Resrouce res = ctx.getResource("file:bean.xml");
Resource res = ctx.getResource("http://localhost:8080/beans.xml");

(三)、ResourceLoaderAware 接口

ResourceLoaderAware接口实现类的实例将获得一个ResourceLoader的引用,ResourceLoaderAware接口也提供了一个setResourceLoader()方法,该方法将由Spring容器负责调用,Spring容器会将一个ResourceLoader对象作为该方法的参数传入。

如果把实现ResourceLoaderAware接口的Bean类部署在Spring容器中,Spring容器会将自身当成ResourceLoader作为setResourceLoader()方法的参数传入。由于ApplicationContext的实现类都实现了ResourceLoader接口,Spring容器自身完全可作为ResorceLoader使用。

第一步 创建类,实现ResourceLoaderAware接口

package com.atguigu.spring6.resouceloader;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;

public class TestBean implements ResourceLoaderAware {

    private ResourceLoader resourceLoader;

    //实现ResourceLoaderAware接口必须实现的方法
	//如果把该Bean部署在Spring容器中,该方法将会有Spring容器负责调用。
	//SPring容器调用该方法时,Spring会将自身作为参数传给该方法。
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    //返回ResourceLoader对象的应用
    public ResourceLoader getResourceLoader(){
        return this.resourceLoader;
    }

}

第二步 创建bean.xml文件,配置TestBean




    

 第三步 测试

package com.atguigu.spring6.resouceloader;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

public class Demo3 {

    public static void main(String[] args) {
        //Spring容器会将一个ResourceLoader对象作为该方法的参数传入
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        TestBean testBean = ctx.getBean("testBean",TestBean.class);
        //获取ResourceLoader对象
        ResourceLoader resourceLoader = testBean.getResourceLoader();
        System.out.println("Spring容器将自身注入到ResourceLoaderAware Bean 中 ? :" + (resourceLoader == ctx));
        //加载其他资源
        Resource resource = resourceLoader.getResource("atguigu.txt");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
    }
}

(四)、使用Resource 作为属性

资源所在的物理位置将被耦合到代码中,如果资源位置发生改变,则必须改写程序。因此,通常建议采用第二种方法,让 Spring 为 Bean 实例依赖注入资源。

第一步 创建依赖注入类,定义属性和方法

package com.atguigu.spring6.resouceloader;

import org.springframework.core.io.Resource;

public class ResourceBean {
    
    private Resource res;
    
    public void setRes(Resource res) {
        this.res = res;
    }
    public Resource getRes() {
        return res;
    }
    
    public void parse(){
        System.out.println(res.getFilename());
        System.out.println(res.getDescription());
    }
}

第二步 创建spring配置文件,配置依赖注入




    
      
      
        
    

 第三步 测试

package com.atguigu.spring6.resouceloader;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Demo4 {

    public static void main(String[] args) {
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("bean.xml");
        ResourceBean resourceBean = ctx.getBean("resourceBean",ResourceBean.class);
        resourceBean.parse();
    }
}

(五)、应用程序上下文和资源路径

实验一:classpath前缀使用

package com.atguigu.spring6.context;
​
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;
​
public class Demo1 {
​
    public static void main(String[] args) {
        /*
         * 通过搜索文件系统路径下的xml文件创建ApplicationContext,
         * 但通过指定classpath:前缀强制搜索类加载路径
         * classpath:bean.xml
         * */
        ApplicationContext ctx =
                new ClassPathXmlApplicationContext("classpath:bean.xml");
        System.out.println(ctx);
        Resource resource = ctx.getResource("atguigu.txt");
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
    }
}

实验二:classpath通配符使用

classpath * :前缀提供了加载多个XML配置文件的能力,当使用classpath*:前缀来指定XML配置文件时,系统将搜索类加载路径,找到所有与文件名匹配的文件,分别加载文件中的配置定义,最后合并成一个ApplicationContext。

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean.xml");
System.out.println(ctx);

当使用classpath * :前缀时,Spring将会搜索类加载路径下所有满足该规则的配置文件。

如果不是采用classpath * :前缀,而是改为使用classpath:前缀,Spring则只加载第一个符合条件的XML文件

注意 :

classpath * : 前缀仅对ApplicationContext有效。实际情况是,创建ApplicationContext时,分别访问多个配置文件(通过ClassLoader的getResource方法实现)。因此,classpath * :前缀不可用于Resource。

使用三:通配符其他使用

一次性加载多个配置文件的方式:指定配置文件时使用通配符

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:bean*.xml");

Spring允许将classpath*:前缀和通配符结合使用:

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean*.xml");

八、数据校验

(一)、通过Validator接口实现

1、引入依赖


    
        org.hibernate.validator
        hibernate-validator
        7.0.5.Final
    

    
        org.glassfish
        jakarta.el
        4.0.1
    

2、创建实体类

package com.atguigu.spring6.validation.method1;

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

3、创建类实现Validator接口,实现接口方法指定校验规则

package com.atguigu.spring6.validation.method1;
​
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
​
public class PersonValidator implements Validator {
​
    @Override
    public boolean supports(Class clazz) {
        return Person.class.equals(clazz);
    }
​
    @Override
    public void validate(Object object, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
        Person p = (Person) object;
        if (p.getAge() < 0) {
            errors.rejectValue("age", "error value < 0");
        } else if (p.getAge() > 110) {
            errors.rejectValue("age", "error value too old");
        }
    }
}

上面定义的类,其实就是实现接口中对应的方法,

supports方法用来表示此校验用在哪个类型上,

validate是设置校验逻辑的地点,其中ValidationUtils,是Spring封装的校验工具类,帮助快速实现校验。

4、使用上述Validator进行测试

package com.atguigu.spring6.validation.method1;
​
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
​
public class TestMethod1 {
​
    public static void main(String[] args) {
        //创建person对象
        Person person = new Person();
        person.setName("lucy");
        person.setAge(-1);
        
        // 创建Person对应的DataBinder
        DataBinder binder = new DataBinder(person);
​
        // 设置校验
        binder.setValidator(new PersonValidator());
​
        // 由于Person对象中的属性为空,所以校验不通过
        binder.validate();
​
        //输出结果
        BindingResult results = binder.getBindingResult();
        System.out.println(results.getAllErrors());
    }
}

(二)、Bean Validation注解实现

1、创建配置类

@Configuration
@ComponentScan("com.atguigu.spring6.validation.method2")
public class ValidationConfig {
​
    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }
}

2、创建实体类,使用注解定义校验规则

package com.atguigu.spring6.validation.method2;
​
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
​
public class User {
​
    @NotNull
    private String name;
​
    @Min(0)
    @Max(120)
    private int age;
​
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

常用注解说明

@NotNull 限制必须不为null

@NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0

@NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串

@DecimalMax(value) 限制必须为一个不大于指定值的数字

@DecimalMin(value) 限制必须为一个不小于指定值的数字

@Max(value) 限制必须为一个不大于指定值的数字

@Min(value) 限制必须为一个不小于指定值的数字

@Pattern(value) 限制必须符合指定的正则表达式

@Size(max,min) 限制字符长度必须在min到max之间

@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

3、使用两种不同的校验器实现

(1)使用jakarta.validation.Validator校验
package com.atguigu.spring6.validation.method2;
​
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
​
@Service
public class MyService1 {
​
    @Autowired
    private Validator validator;
​
    public  boolean validator(User user){
        Set> sets =  validator.validate(user);
        return sets.isEmpty();
    }
​
}
(2)使用org.springframework.validation.Validator校验
package com.atguigu.spring6.validation.method2;
​
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindException;
import org.springframework.validation.Validator;
​
@Service
public class MyService2 {
​
    @Autowired
    private Validator validator;
​
    public boolean validaPersonByValidator(User user) {
        BindException bindException = new BindException(user, user.getName());
        validator.validate(user, bindException);
        return bindException.hasErrors();
    }
}

4、 测试

package com.atguigu.spring6.validation.method2;
​
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
​
public class TestMethod2 {
​
    @Test
    public void testMyService1() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService1 myService = context.getBean(MyService1.class);
        User user = new User();
        user.setAge(-1);
        boolean validator = myService.validator(user);
        System.out.println(validator);
    }
​
    @Test
    public void testMyService2() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService2 myService = context.getBean(MyService2.class);
        User user = new User();
        user.setName("lucy");
        user.setAge(130);
        user.setAge(-1);
        boolean validator = myService.validaPersonByValidator(user);
        System.out.println(validator);
    }
}

(三)、基于方法实现校验

1、 创建配置类,配置MethodValidationPostProcessor

package com.atguigu.spring6.validation.method3;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
​
@Configuration
@ComponentScan("com.atguigu.spring6.validation.method3")
public class ValidationConfig {
​
    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

2、 创建实体类,使用注解设置校验规则

package com.atguigu.spring6.validation.method3;
​
import jakarta.validation.constraints.*;
​
public class User {
​
    @NotNull
    private String name;
​
    @Min(0)
    @Max(120)
    private int age;
​
    @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
    @NotBlank(message = "手机号码不能为空")
    private String phone;
​
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
}

3、 定义Service类,通过注解操作对象

package com.atguigu.spring6.validation.method3;
​
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
​
@Service
@Validated
public class MyService {
    
    public String testParams(@NotNull @Valid User user) {
        return user.toString();
    }
​
}

4、测试

package com.atguigu.spring6.validation.method3;
​
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
​
public class TestMethod3 {
​
    @Test
    public void testMyService1() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService myService = context.getBean(MyService.class);
        User user = new User();
        user.setAge(-1);
        myService.testParams(user);
    }
}

(四)、实现自定义校验

1、自定义校验注解

package com.atguigu.spring6.validation.method4;
​
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
​
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {CannotBlankValidator.class})
public @interface CannotBlank {
    //默认错误消息
    String message() default "不能包含空格";
​
    //分组
    Class[] groups() default {};
​
    //负载
    Class[] payload() default {};
​
    //指定多个时使用
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        CannotBlank[] value();
    }
}

2、编写真正的校验类

package com.atguigu.spring6.validation.method4;
​
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
​
public class CannotBlankValidator implements ConstraintValidator {
​
        @Override
        public void initialize(CannotBlank constraintAnnotation) {
        }
​
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
                //null时不进行校验
                if (value != null && value.contains(" ")) {
                        //获取默认提示信息
                        String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
                        System.out.println("default message :" + defaultConstraintMessageTemplate);
                        //禁用默认提示信息
                        context.disableDefaultConstraintViolation();
                        //设置提示语
                        context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
                        return false;
                }
                return true;
        }
}

你可能感兴趣的:(spring,java,后端)