spring框架

1. 核心概念介绍

  1. IoC容器

    IoC(Inversion of Control)控制反转,它是一种设计思想,能够指导我们如何设计出松耦合、更优良的程序。Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。

  2. DI

    指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。包括set方式注入和构造器方式注入。

2. bean管理

bean管理即为Bean对象的创建,以及Bean对象中属性的赋值。

1. 基于XML方式管理bean

  1. 添加依赖。其中groupId表示该依赖项所属的组织或项目。artifactId是依赖项的具体模块或项目名。在这里,spring-context表示Spring框架中的上下文模块,该模块包含Spring核心的IoC容器及相关功能。
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    <version>6.0.2</version>
</dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.6.3</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.19.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
        <version>2.19.0</version>
    </dependency>
<dependencies>
  1. 获取bean对象

    • 根据id获取

    例如其中的id=“user”。若未显式地给出id值,则为类名首字母小写,其余同类名,举例:MyService->myService.

    <bean id="user" class="com.guang.User"></bean>
    
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    User user = (User)context.getBean("user");
    
    • 根据类型获取(相比“根据id获取”有避免id冲突便于重构的优势)
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    User user = context.getBean(User.class);
    
    • 根据id和类型获取
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    User user = context.getBean("user", User.class)
    
    • 扩展知识

      1. 可以根据接口类型可以获取 bean 吗?

      可以,前提是bean唯一。

      1. 如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?

      不行,因为bean不唯一

  2. 依赖注入

  • set方式
package com.guang.di;

public class Book {
    private String bookName;
    private String author;
    private String others;
    // set方法

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

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

    public String getOthers() {
        return others;
    }

    public void setOthers(String others) {
        this.others = others;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                ", author='" + author + '\'' +
                ", others='" + others + '\'' +
                '}';
    }
}
<bean id="book" class="com.guang.di.Book">
<!-- 本质就是调用set方法 -->
 <property name="bookName" value="概念漂移"></property>
 <property name="author" value="xxy"></property>
</bean>
  • 构造器方式
public class Book {
 private String bookName;
 private String author;
 private String others;

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

 @Override
 public String toString() {
     return "Book{" +
             "bookName='" + bookName + '\'' +
             ", author='" + author + '\'' +
             ", others='" + others + '\'' +
             '}';
 }
}
<bean id="book2" class="com.guang.di.Book">
 <constructor-arg name="author" value="概念漂移"></constructor-arg>
 <constructor-arg name="bookName" value="zxr"></constructor-arg>
</bean>
  • 特殊值
<!--null值-->
<property name="others">
 <null></null>
</property>
<!--小于号大于号-->
<!--1.使用xml实体-->
<!--<property name="expression" value="a < b"/>-->
<property name="expression" value="a &lt; b"/>

<!--2.使用CDATA节-->
<property name="expression">
    <!-- 解决方案二:使用CDATA节 -->
    <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
    <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
    <!-- 所以CDATA节中写什么符号都随意 -->
    <value><![CDATA[a < b]]></value>
</property>
  • 为对象类型属性赋值

java类:

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

    private Department department;
}

public class Department {
    private String name;
}

配置文件:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--方式一 外部bean注入,创建两个类对象,在employee的bean标签中使用property引入department的bean-->
    <!--所谓的引入外部bean即指该标签之外的bean-->
<!--    <bean id="employee" class="com.guang.di.Employee">-->
<!--        <property name="age" value="18"></property>-->
<!--        <property name="name" value="xxy"></property>-->
<!--        &lt;!&ndash;对象类型属性注入&ndash;&gt;-->
<!--        <property name="department" ref="department"></property>-->
<!--如果错把ref属性写成了value属性,会抛出异常: Caused by: java.lang.IllegalStateException: Cannot convert value of type ‘java.lang.String’ to required type ‘com.atguigu.spring6.bean.Clazz’ for property ‘clazz’: no matching editors or conversion strategy found
意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值-->
<!--    </bean>-->

<!--    <bean id="department" class="com.guang.di.Department">-->
<!--        <property name="name" value="维修部"></property>-->
<!--    </bean>-->


    <!--    方式二 内部bean注入-->
    <!--   相对于外部bean注入 -->
<!--    <bean id="employee" class="com.guang.di.Employee">-->
<!--        <property name="age" value="18"></property>-->
<!--        <property name="name" value="xxy"></property>-->
<!--        &lt;!&ndash;对象类型属性注入&ndash;&gt;-->
<!--        <property name="department">-->
<!--            <bean id="department" class="com.guang.di.Department">-->
<!--                <property name="name" value="外卖部"></property>-->
<!--            </bean>-->
<!--        </property>-->
<!--    </bean>-->
    
        <!-- 方式三 级联赋值   -->
        <!-- 即使在department中不进行属性注入也没问题      -->
        <bean id="employee" class="com.guang.di.Employee">
                <property name="age" value="18"></property>
                <property name="name" value="xxy"></property>
                <!--对象类型属性注入-->
                <property name="department" ref="department"></property>
                <property name="department.name" value="开发部门"></property>
        </bean>

        <bean id="department" class="com.guang.di.Department">
<!--            <property name="name" value="测试部"></property>-->
        </bean>
</beans>
  • 为数组类型属性赋值

java类:

public class Employee {
    private String name;
    private int age;
    private String[] hobby;
}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="employee" class="com.guang.di.Employee">
        <property name="name" value="xxy"></property>
        <property name="age" value="18"></property>

        <!--数组类型-->
        <property name="hobby">
            <array>
                <value>抽烟</value>
                <value>喝酒</value>
                <value>烫头</value>
            </array>
        </property>
    </bean>
</beans>
  • 为List, set集合类型属性赋值

java类:

public class Department {
    private String name;
    private List<Employee> employees;
}

public class Employee {
    private String name;
    private int age;
    private String[] hobby;

    private Department department;
}

配置文件(若为Set集合类型属性赋值,只需要将其中的list标签改为set标签即可)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="employee1" class="com.guang.di.Employee">
        <property name="name" value="yg"></property>
    </bean>

    <bean id="employee2" class="com.guang.di.Employee">
        <property name="name" value="yqh"></property>
    </bean>

    <bean id="employee3" class="com.guang.di.Employee">
        <property name="name" value="xwk"></property>
    </bean>

    <bean id="employee4" class="com.guang.di.Employee">
        <property name="name" value="xxy"></property>
    </bean>

    <bean id="department" class="com.guang.di.Department">
        <property name="name" value="研发部"></property>
        <property name="employees" >
            <list>
                <ref bean="employee1"></ref>
                <ref bean="employee2"></ref>
                <ref bean="employee3"></ref>
                <ref bean="employee4"></ref>

<!--                这里可以仿照内部bean注入的方法-->
<!--                <bean id="employee1" class="com.guang.di.Employee">-->
<!--                    <property name="name" value="yg"></property>-->
<!--                </bean>-->
<!--                <bean id="employee1" class="com.guang.di.Employee">-->
<!--                    <property name="name" value="xxy"></property>-->
<!--                </bean>-->
            </list>
        </property>
    </bean>
</beans>
  • 为Map集合类型属性赋值

java类:

public class Student {
    private String sId;
    private String sName;
    private Map<String, Teacher> teacherMap;
}

public class Teacher {
    private String tId;
    private String tName;
}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.guang.di.Student">
        <property name="sId" value="123"></property>
        <property name="sName" value="yg"></property>
        <property name="teacherMap">
            <map>
                <entry>
                    <key>
                        <value>123</value>
                    </key>
                    <ref bean="teacher1"></ref>
                </entry>
                <entry>
                    <key>
                        <value>123</value>
                    </key>
                    <ref bean="teacher2"></ref>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="teacher1" class="com.guang.di.Teacher">
        <property name="tId" value="123"></property>
        <property name="tName" value="zls"></property>
    </bean>

    <bean id="teacher2" class="com.guang.di.Teacher">
        <property name="tId" value="456"></property>
        <property name="tName" value="wls"></property>
    </bean>
</beans>

2. 基于注解方式管理bean

  1. 引入依赖
<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.6.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.19.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j2-impl</artifactId>
            <version>2.19.0</version>
        </dependency>

        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
</dependencies>
  1. 开启组件扫描

    • 基本的扫描方式
    <!--开启组件扫描-->
    <context:component-scan base-package="com.guang"></context:component-scan>
    
    • 指定要排除的组件
    <context:component-scan base-package="com.atguigu.spring6">
        <!-- context:exclude-filter标签:指定排除规则 -->
        <!-- 
     		type:设置排除或包含的依据
    		type="annotation",根据注解排除,expression中设置要排除的注解的全类名
    		type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    	-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
    </context:component-scan>
    
    • 仅扫描指定组件
    <context:component-scan base-package="com.atguigu" use-default-filters="false">
        <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
        <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
        <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
        <!-- 
     		type:设置排除或包含的依据
    		type="annotation",根据注解排除,expression中设置要排除的注解的全类名
    		type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    	-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    	<!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
    </context:component-scan>
    
  2. 使用注解定义bean

注解说明
@Component该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
@Repository该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

示例:

User.java

@Component(value = "user")
public class User {
}

TestUser.java

public class TestUser {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); 
        User user = context.getBean(User.class);
        System.out.println(user);
    }
}
  1. 依赖注入

    • 属性注入

    UserDao接口及实现类:

      package com.guang.dao;
    
      public interface UserDao {
          public void add();
      }
    
      @Repository
      public class UserDaoImpl implements UserDao{
          @Override
          public void add() {
              System.out.println("dao");
          }
      }
    

    UserService接口及实现类

      public interface UserService {
          public void add();
      }
    
      @Service
      public class UserServiceImpl implements UserService{
          @Autowired
          private UserDao userDao;
    
          @Override
          public void add() {
              System.out.println("service");
              userDao.add();
          }
      }
    

    UserController

      @Controller
      public class UserController {
        @Autowired
          private UserService userService;
    
          public void add(){
              System.out.println("controller");
              userService.add();
          }
      }
    

    TestUserController

       public class TestUserController {
           public static void main(String[] args) {
               ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
               UserController controller = context.getBean(UserController.class);
               controller.add();
           }
       }
    
    • set方法注入

    UserDao接口及实现类

    package com.guang.dao;
    
    public interface UserDao {
        public void add();
    }
    
    @Repository
    public class UserDaoImpl implements UserDao{
        @Override
        public void add() {
            System.out.println("dao");
        }
    }
    

    UserService接口及实现类

    public interface UserService {
        public void add();
    }
    
    @Service
    public class UserServiceImpl implements UserService{
    
        private UserDao userDao;
    
        @Autowired
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add() {
            System.out.println("service");
            userDao.add();
        }
    }
    

    UserController

    @Controller
    public class UserController {
    
        private UserService userService;
    
        @Autowired
        public void setUserController(UserService userService) {
            this.userService = userService;
        }
    
        public void add(){
            System.out.println("controller");
            userService.add();
        }
    }
    

    TestUserController

    public class TestUserController {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            UserController controller = context.getBean(UserController.class);
            controller.add();
        }
    }
    
    • 构造方法
    package com.guang.dao;
    
    public interface UserDao {
        public void add();
    }
    
    @Repository
    public class UserDaoImpl implements UserDao{
        @Override
        public void add() {
            System.out.println("dao");
        }
    }
    

    UserService接口及实现类

    public interface UserService {
        public void add();
    }
    
    @Service
    public class UserServiceImpl implements UserService{
    
        private UserDao userDao;
    
        @Autowired
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add() {
            System.out.println("service");
            userDao.add();
        }
    }
    

    UserController

    @Controller
    public class UserController {
    
        private UserService userService;
    
        @Autowired
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        public void add(){
            System.out.println("controller");
            userService.add();
        }
    }
    

    TestUserController

    public class TestUserController {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            UserController controller = context.getBean(UserController.class);
            controller.add();
        }
    }
    
    • 形参上注入
    package com.guang.dao;
    
    public interface UserDao {
        public void add();
    }
    
    @Repository
    public class UserDaoImpl implements UserDao{
        @Override
        public void add() {
            System.out.println("dao");
        }
    }
    

    UserService接口及实现类

    public interface UserService {
        public void add();
    }
    
    @Service
    public class UserServiceImpl implements UserService{
    
        private UserDao userDao;
    
        public UserServiceImpl(@Autowired UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add() {
            System.out.println("service");
            userDao.add();
        }
    }
    

    UserController

    @Controller
    public class UserController {
    
        private UserService userService;
    
        public UserController(@Autowired UserService userService) {
            this.userService = userService;
        }
    
        public void add(){
            System.out.println("controller");
            userService.add();
        }
    }
    

    TestUserController

    public class TestUserController {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            UserController controller = context.getBean(UserController.class);
            controller.add();
        }
    }
    
    • 只有一个构造方法,不使用@Autowired也可
    • 使用@Autowired和@Qualifier注解,实现按名称注入

    UserDao接口及实现类

    package com.guang.dao;
    
    public interface UserDao {
        public void add();
    }
    
    @Repository
    public class UserDaoImpl implements UserDao{
        @Override
        public void add() {
            System.out.println("dao");
        }
    }
    
    @Repository(value = "userRedisDaoImpl")
    public class UserRedisDaoImpl implements UserDao {
        @Override
        public void add() {
            System.out.println("redis dao");
        }
    }
    

    UserService接口及实现类

    public interface UserService {
        public void add();
    }
    
    @Service
    public class UserServiceImpl implements UserService{
    
        @Autowired
        @Qualifier(value = "userRedisDaoImpl")
        private UserDao userDao;
    
        public UserServiceImpl() {
        }
    
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add() {
            System.out.println("service");
            userDao.add();
        }
    }
    

    UserController

    @Controller
    public class UserController {
        @Autowired
        private UserService userService;
    
        public void add(){
            System.out.println("controller");
            userService.add();
        }
    }
    

    TestUserController

    public class TestUserController {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            UserController controller = context.getBean(UserController.class);
            controller.add();
        }
    }
    
    • @Resource注解

    @Resource注解默认根据@Resource(name="")名称装配byName,未指定name时,根据属性名进行装配。通过name找不到的话会自动启动通过类型byType装配。

    @Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解一起用。

    @Resource注解用在属性上、setter方法上。
    @Autowired注解用在属性上、setter方法上、构造方法上、构造方法参数上。

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

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId >jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>
UserDao接口及其实现类

```java
public interface UserDao {
    public void add();
}

@Repository(value = "myUserDao")
public class UserDaoImpl implements UserDao{
    @Override
    public void add() {
        System.out.println("dao");
    }
}
```

UserService接口及其实现类

```java
public interface UserService {
    public void add();
}

@Service("myUserService")
public class UserServiceImpl implements UserService{

    @Resource
    private UserDao myUserDao;

    public UserServiceImpl() {
    }

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

    @Override
    public void add() {
        System.out.println("service");
        myUserDao.add();
    }
}
```

UserController

```java
@Controller
public class UserController {

    @Resource
    private UserService userService;
public UserController(UserService userService) {
	this.userService = userService;
}

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

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

TestUserController

```java
public class TestUserController {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserController controller = context.getBean(UserController.class);
        controller.add();
    }
}
```
  1. 全注解开发

使用配置类代替原有的用于开启组件扫描的xml配置文件

@Configuration
@ComponentScan("com.guang")
public class SpringConfig {
}

TestUserController

public class TestUserController {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController controller = context.getBean(UserController.class);

        controller.add();
    }
}

3. AOP

1. 概念介绍

横切关注点:横切关注点是指那些分散在系统各个模块中、难以通过面向对象编程单独封装的功能或逻辑。它们通常与核心业务逻辑无关,但在多个模块中重复出现,比如日志记录、权限控制、事务管理等。通俗而讲,就是要实现的功能。

切面:定义了横切关注点。

切入点:即需要增强的方法。

通知:通知是在目标方法的特定切入点处执行的动作。

2. 基于注解方式

接口及实现类:

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);

}

xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.guang.aop.annotation"></context:component-scan>
    <!--开启aspectj自动代理,为目标对象生成代理对象。通俗而讲,就是识别@Aspect注解-->
    <aop:aspectj-autoproxy />
</beans>

切面类LogAspect:

包括通知类型切入点表达式规则及重用。其中环绕类型可视为其余类型的功能集合。

// @Aspect表示这个类是一个切面类
@Aspect
// IoC容器管理
@Component
public class LogAspect {
    // 设置切入点何通知类型
    // 切入点表达式:execution (访问修饰符
    //                          增强方法返回类型(其中*表示任意)
    //                          方法所在类型的全类名(其中*表示包名任意,*..表示包名任意同时包的层次深度任意)
    //                          方法名(其中*类似通配符)
    //                          方法参数(其中..表示参数列表任意))
    // 通知类型:
    // 前置 @Before(value = "切入点表达式配置切入点")
    @Before(value = "execution(public int com.guang.aop..CalculatorImpl.*(..))")
    public void beforeMethod() {
        System.out.println("前置通知");
    }

    // 返回 @AfterReturning,可得到增强方法的返回值, 返回通知在后置通知之前
    @AfterReturning(value = "execution(public int com.guang.aop..CalculatorImpl.*(..))", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();

        System.out.println("返回通知" + name + Arrays.toString(args) + result);
    }

    // 异常 @AfterThrowing
    @AfterThrowing(value = "execution(public int com.guang.aop..CalculatorImpl.*(..))", throwing = "ex")
    public void afterThrowingMethod1(JoinPoint joinPoint, Throwable ex){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();

        System.out.println("返回通知" + name + Arrays.toString(args) + ex);
    }

    // 后置 @After()
    @After(value = "pointCut()")
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println(methodName + "后置通知" + Arrays.toString(args));
    }

    // 环绕 @Around()
    @Around(value = "execution(public int com.guang.aop.annotation.CalculatorImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Object result = null;
        try{
            System.out.println("环绕通知==目标方法返回值之前");
            result = joinPoint.proceed(args);
            System.out.println("环绕通知==目标方法返回值之后");
        }catch (Throwable e){
            e.printStackTrace();
            System.out.println("环绕通知==目标方法出现异常执行");
        }finally {
            System.out.println("环绕通知==目标方法执行完毕");
        }
        System.out.println(result);
        return result;
    }

    // 重用切入点表达式
    @Pointcut(value = "execution(public int com.guang.aop.annotation.CalculatorImpl.*(..))")
    public void pointCut() {

    }
}

3. 基于xml方式

接口及实现类:

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);

}

@Component
public class CalculatorImpl implements Calculator {

    @Override
    public int add(int i, int j) {

        int result = i + j;
//        int example = 1/0;
        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;
    }
}

xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


    <!--开启组件扫描-->
    <context:component-scan base-package="com.guang.aop.xml"/>

    <aop:config>
        <!--配置切面类-->
        <aop:aspect ref="logAspect">
            <!--配置切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.guang.aop.xml.LogAspect.*(..))"/>
            <!--配置五种通知类型-->
            <!--pointcut-ref参数指定切入点-->
            <aop:before method="beforeMethod" pointcut-ref="pointcut"></aop:before>

            <aop:after method="afterMethod" pointcut-ref="pointcut"></aop:after>
            <!--returning参数中的值要和fterReturningMethod方法中的统一-->
            <aop:after-returning method="afterReturningMethod" returning="result" pointcut-ref="pointcut"></aop:after-returning>

            <aop:after-throwing method="afterThrowingMethod1" pointcut-ref="pointcut" throwing="ex"></aop:after-throwing>

            <aop:around method="aroundMethod" pointcut-ref="pointcut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

LogAspect切面类:

@Component(value = "logAspect")
public class LogAspect {
    // 通知类型:
    // 前置通知
    public void beforeMethod() {
        System.out.println("前置通知");
    }

    // 返回通知
    // 返回 @AfterReturning,可得到增强方法的返回值, 返回通知在后置通知之前
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("返回通知" + name + Arrays.toString(args) + result);
    }

    // 异常通知
    public void afterThrowingMethod1(JoinPoint joinPoint, Throwable ex){
        String name = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("返回通知" + name + Arrays.toString(args) + ex);
    }

    // 后置通知
    public void afterMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println(methodName + "后置通知" + Arrays.toString(args));
    }

    // 环绕通知
    public Object aroundMethod(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        Object result = null;
        try{
            System.out.println("环绕通知==目标方法返回值之前");
            result = joinPoint.proceed(args);
            System.out.println("环绕通知==目标方法返回值之后");
        }catch (Throwable e){
            e.printStackTrace();
            System.out.println("环绕通知==目标方法出现异常执行");
        }finally {
            System.out.println("环绕通知==目标方法执行完毕");
        }
        System.out.println(result);
        return result;
    }

    // 重用切入点表达式
    @Pointcut(value = "execution(public int com.guang.aop.xml.CalculatorImpl.*(..))")
    public void pointCut() {

    }
}

4. Spring整合Junit4, Junit5

  1. Junit5

添加依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>6.0.2</version>
</dependency>

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.0</version>
</dependency>

示例类:

@Component
public class User {
    public void run(){
        System.out.println("run");
    }
}

测试类:

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

    @Autowired
    private User user;

    @Test
    public void test() {
        System.out.println(user);
        user.run();
    }
}
  1. Junit4

添加依赖:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
</dependency>

实例类:

@Component
public class User {
    public void run(){
        System.out.println("run");
    }
}

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:bean.xml")
public class SpringTestJunit4 {
    @Autowired
    private User user;
    
	// import org.junit.Test;
   @Test
   public void test() {
       System.out.println(user);
       user.run();
   }
}

5. 事务管理

事务具有ACID原则。

1. 基于注解方式管理

  1. 示例:
    bean.xml:
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--组件扫面-->
    <context:component-scan base-package="com.guang"/>
    <!--引入外部属性文件,创建数据源对象-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--1.创建数据源对象-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--2.创建jdbcTemplate对象,注入数据源-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--
    开启事务的注解驱动
    通过注解@Transactional所标识的方法或标识的类中所有的方法,都会被事务管理器管理事务
    -->
    <!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

BookDao, BookDaoImpl:

public interface BookDao {
    // 更新余额
    Integer updateUserBalance(Integer userId, Integer price);
    // 更新图书库存量
    Integer updateStock(Integer bookId);
    // 根据图书id查看价格
    Integer getBookPriceByBookId(Integer bookId);
}

@Repository
public class BookDaoImpl implements BookDao{

    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 更新图书库存量
    @Override
    public Integer updateStock(Integer bookId) {
        String sql = "update t_book set stock = stock - 1 where book_id = ?";
        Integer stock = jdbcTemplate.update(sql, bookId);
        return stock;
    }

    // 根据图书id查看价格
    @Override
    public Integer getBookPriceByBookId(Integer bookId) {
        String sql = "select price from t_book where book_id = ?";
        Integer price = jdbcTemplate.queryForObject(sql, Integer.class, bookId);
        return price;
    }

    // 更新余额
    @Override
    public Integer updateUserBalance(Integer userId, Integer price) {
        String sql = "update t_user set balance = balance - ? where user_id = ?";
        Integer balance = jdbcTemplate.update(sql, price, userId);
        return balance;
    }
}

BookService, BookServiceImpl:

public interface BookService {
    void buyBook(Integer bookId, Integer userId);
}

@Service
@Transactional
// @Transactional标识在方法上,则只会影响该方法
// @Transactional标识的类上,则会影响类中所有的方法
public class BookServiceImpl implements BookService{

    @Autowired
    private BookDao bookDao;

    @Override
    public void buyBook(Integer bookId, Integer userId) {
        // 根据图书id查看价格
        Integer price = bookDao.getBookPriceByBookId(bookId);
        // 更新图书库存量
        bookDao.updateStock(bookId);
        // 更新余额
        bookDao.updateUserBalance(userId, price);
    }
}

BookController:

@Controller
public class BookController {

    @Autowired
    private BookService bookService;

    public void buyBook(Integer bookId, Integer userId){
        bookService.buyBook(bookId, userId);
    }
}
  1. 事务属性

    1. 只可进行只读操作:
    @Transactional(readOnly = true)
    
    1. 超时设置
    @Transactional(timeout = 3) //单位为秒
    
    1. 回滚策略
    • rollbackFor:需要设置一个class对象
    • rollbackForClassName:需要设置一个字符串类型的全类名
    • noRollbackFor:需要设置一个class对象
    • rollbackFor:需要设置一个字符串类型的全类名
    1. 隔离级别
    隔离级别脏读不可重复读幻读
    READ UNCOMMITTED
    READ COMMITTED
    REPEATABLE READ
    SERIALIZABLE
  2. 传播行为

  • REQUIRED, 没有就新建,有就加入
  • SUPPORT
  • MANDATORY
  • REQUIRES_NEW, 开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起
  • NOT_SUPPORTED
  • NEVER
  • NESTED

Checkout接口及实现类:

public interface CheckoutService {
    void checkout(Integer[] bookIds, Integer userId);
}

@Service
public class CheckoutServiceImpl implements CheckoutService {

    @Autowired
    private BookService bookService;

    @Override
    @Transactional
    // 购买多本书
    public void checkout(Integer[] bookIds, Integer userId) {
        for(Integer bookId : bookIds) {
            bookService.buyBook(bookId, userId);
        }
    }
}

BookService接口及其实现类:

public interface BookService {
    void buyBook(Integer bookId, Integer userId);
}

@Service
@Transactional(timeout = 3)
public class BookServiceImpl implements BookService{

    @Autowired
    private BookDao bookDao;

    @Override
    public void buyBook(Integer bookId, Integer userId) {
        // 根据图书id查看价格
        Integer price = bookDao.getBookPriceByBookId(bookId);
        // 更新图书库存量
        bookDao.updateStock(bookId);
        // 更新余额
        bookDao.updateUserBalance(userId, price);
    }
}

对于上述代码的解释,BookService的buyBook方法为某用户购买一本书的方法,CheckoutService的checkout方法为某用户购买多本书的方法。在这两个方法上均有@Transactional注解。那么在一个事务中调用另一事务的不同行为即为不同传播行为设置。

  1. 全注解开发

即将xml配置文件写为配置类的形式。

SpringConfig配置类:

// 表明这是一个配置类
@Configuration
@ComponentScan("com.guang")
@EnableTransactionManagement
public class SpringConfig {

    @Bean
    // @Bean注解用于将一个方法的返回值注册为Spring应用程序上下文中的一个Bean,帮助开发者显示的定义和创建bean
    public DataSource getDataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
        ds.setUsername("root");
        ds.setPassword("123456");
        return ds;
    }

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

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

测试类:

public class TestAnno {

    @Test
    public void testTxAllAnnotation(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookController bookController = context.getBean("bookController", BookController.class);
        Integer[] bookIds = {1, 2};
        Integer userId = 1;
        bookController.checkout(bookIds, userId);
    }
}

2. 基于xml方式管理

  1. 示例

xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启组件扫描-->
    <context:component-scan base-package="com.guang.xmltx"/>

    <!--引入外部属性文件,创建数据源对象-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--创建数据源对象-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--创建jdbcTemplate对象,注入数据源-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="druidDataSource"/>
    </bean>

    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="druidDataSource"></property>
    </bean>

    <!--配置事务增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"></tx:method>
            <tx:method name="update*" read-only="false" propagation="REQUIRED"></tx:method>
            <tx:method name="buy*" read-only="false" propagation="REQUIRED"></tx:method>
        </tx:attributes>
    </tx:advice>

    <!--配置切入点和通知使用的方法-->
    <aop:config>
        <!--其中第二个*为任意方法-->
        <aop:pointcut id="pt" expression="execution(* com.guang.xmltx.service.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>

BookDao, BookDaoImpl:

public interface BookDao {
    // 更新余额
    Integer updateUserBalance(Integer userId, Integer price);
    // 更新图书库存量
    Integer updateStock(Integer bookId);
    // 根据图书id查看价格
    Integer getBookPriceByBookId(Integer bookId);
}

@Repository
public class BookDaoImpl implements BookDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 更新图书库存量
    @Override
    public Integer updateStock(Integer bookId) {
        String sql = "update t_book set stock = stock - 1 where book_id = ?";
        Integer stock = jdbcTemplate.update(sql, bookId);
        return stock;
    }

    // 根据图书id查看价格
    @Override
    public Integer getBookPriceByBookId(Integer bookId) {
        String sql = "select price from t_book where book_id = ?";
        Integer price = jdbcTemplate.queryForObject(sql, Integer.class, bookId);
        return price;
    }

    // 更新余额
    @Override
    public Integer updateUserBalance(Integer userId, Integer price) {
        String sql = "update t_user set balance = balance - ? where user_id = ?";
        Integer balance = jdbcTemplate.update(sql, price, userId);
        return balance;
    }
}

BookService, BookServiceImpl:

public interface BookService {
    void buyBook(Integer bookId, Integer userId);
}

@Service
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao bookDao;

    @Override
    public void buyBook(Integer bookId, Integer userId) {

        // 根据图书id查看价格
        Integer price = bookDao.getBookPriceByBookId(bookId);
        // 更新图书库存量
        bookDao.updateStock(bookId);
        // 更新余额
        bookDao.updateUserBalance(userId, price);
    }
}

BookController:

@Controller
public class BookController {
    @Autowired
    private BookService bookService;

    public void buyBook(Integer bookId, Integer userId){
        bookService.buyBook(bookId, userId);
    }
}

测试类:

@SpringJUnitConfig(locations = "classpath:beans-xml.xml")
public class TestBookTx {
    @Autowired
    private BookController bookController;

    @Test
    public void test() {
        bookController.buyBook(1, 1);
    }
}

6. 资源操作

1.Resource接口

  1. UrlResource实现类测试
// UrlResource访问网络资源
public class UrlResourceDemo {
    // http
    public static void loadUrlResource(String path) {
        // 1.创建Resource接口实现类UrlResource
        try {
            UrlResource url = new UrlResource(path);
            // 2. 获取资源信息
            System.out.println(url.getFilename());
            System.out.println(url.getURL());
            System.out.println(url.getDescription());
            System.out.println(url.getInputStream().read());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        // http前缀
//        loadUrlResource("https://www.baidu.com/");
//        file前缀
        loadUrlResource("file:tmp.txt");

    }
}
  1. ClassPathResource实现类测试
// 访问类路径下资源, 如:target/classes
public class ClassPathResourceDemo {
    public static void loadClassPathResource(String path) {
        ClassPathResource classPathResource = new ClassPathResource(path);
        System.out.println(classPathResource.getFilename());
        System.out.println(classPathResource.getDescription());
        try {
            InputStream in = classPathResource.getInputStream();
            byte[] bytes = new byte[1024];
            while (in.read(bytes) != -1) {
                System.out.println(new String(bytes));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        loadClassPathResource("tmp1.txt");
    }
}
  1. FileSystemResource实现类测试
// 访问文件系统资源
public class FileSystemResourceDemo {
    public static void loadFileResource(String path){
        // 创建对象
        FileSystemResource fileSystemResource = new FileSystemResource(path);
        System.out.println(fileSystemResource.getFilename());
        System.out.println(fileSystemResource.getDescription());
        try {
            InputStream inputStream = fileSystemResource.getInputStream();
            byte[] bytes = new byte[1024];
            while (inputStream.read(bytes)!=-1){
                System.out.println(new String(bytes));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        loadFileResource("G:\\实验室项目文件\\新建文本文档.txt");
    }
}

2.ResourceLoader接口

ResourceLoader接口的实现类可通过getResource()方法得到Resource实例,通常都是如此得到Resource实例。

实现类测试:

public class ResourceLoaderDemo {

    @Test
    public void test() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
        Resource resource = context.getResource("classpath:tmp.txt");
        System.out.println(resource.getFilename());
    }

    @Test
    public void test2() 
         // ApplicationContext接口扩展了ResourceLoader接口
        ApplicationContext context =new FileSystemXmlApplicationContext();
        Resource resource = context.getResource("classpath:tmp.txt");
        System.out.println(resource.getFilename());
    }
}

3. ResourceLoaderAware接口

4. 使用Resource作为属性

将传入路径直接创建Resource实例或从Application中得到Resource实例这一步骤分离出来,写在配置文件之中。

xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="resourceBean" class="com.guang.di.ResourceBean">
        <!--当你在XML配置中使用<property name="resource" value="classpath:tmp1.txt">时,
        Spring会将value属性中的字符串(即资源路径)转换为相应的Resource对象。Spring内部有一个
        Resource解析器,能够识别以classpath:、file:等前缀开头的资源路径,并自动创建相应的Resource实现类-->
        <property name="resource" value="classpath:tmp1.txt"></property>
    </bean>
</beans>

ResourceBean:

public class ResourceBean {
    private Resource resource;

    public Resource getResource() {
        return resource;
    }

    public void setResource(Resource resource) {
        this.resource = resource;
    }

    public void parse(){
        System.out.println(resource.getFilename());
        System.out.println(resource.getDescription());
    }
}

测试文件:

我认为即用getBean()替代了原先的getResource()方法。

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

5. Application确定资源访问策略

  1. 使用Application实现类指定访问策略

当程序创建ApplicationContext实例时,通常也是以Resource的方式来访问配置文件的,即ApplicationContext的实现类ClassPathXmlApplicationContext, FileSystemXmlApplicationContext等。

  1. 使用前缀指定访问策略

    1. classpath和classpath*区别:

    classpath:只会从 单一的类路径 中加载资源文件。classpath*: 会从 所有的类路径JAR 包 中查找匹配的资源文件,并且支持加载 多个匹配的文件

    1. 可使用*通配符,如ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean*.xml");

    2. 可使用ApplicationContext context = new ClassPathXmlApplicationContext("classpath:**/*.xml");来加载任意层次的 xml文件。

7. 国际化i18n

1. Java国际化

示例:

public class ResourceI18n {
    public static void main(String[] args) {
        // Locale:表示特定的地理、政治或文化区域
        // ResourceBundle:用于加载特定区域的资源文件,以支持国际化

        // 根据给定的基本名称和特定区域设置加载资源包
        ResourceBundle bundle = ResourceBundle.getBundle("message", new Locale("zh", "CN"));
        String value1 = bundle.getString("test");
        System.out.println(value1);

        ResourceBundle bundle1 = ResourceBundle.getBundle("message", new Locale("en", "GB"));
        String value2 = bundle1.getString("test");
        System.out.println(value2);
    }
}

2. Spring国际化

xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>atguigu</value>
            </list>
        </property>
        <property name="defaultEncoding">
            <value>utf-8</value>
        </property>
    </bean>
</beans>

atguigu_zh_CN.properties:

www.atguigu.com=欢迎{0}时间{1}

测试文件:

public class ResourceI18n {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
        Object[] objs = {"atguigu", new Date().toString()};
        String message = context.getMessage("www.atguigu.com", objs, Locale.CHINA);
        System.out.println(message);
    }
}

8. 数据校验 Validation

1. 实现Validator接口

person:

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;
    }
}

PersonValidator:

public class PersonValidator implements Validator {
    @Override
    // 用来表示此校验用在哪个类型上
    public boolean supports(Class<?> clazz) {
        return Person.class.equals(clazz);
    }

    @Override
    // 设置校验逻辑
    public void validate(Object target, Errors errors) {
        // 判断name不能为空
        ValidationUtils.rejectIfEmpty(errors, "name", "name.empty" , "name is null");
        // 0 < age < 100
        Person person = (Person) target;
        if(person.getAge() < 0){
            errors.rejectValue("age", "age.value.error" , "age < 0");
        }else if(person.getAge() > 100){
            errors.rejectValue("age", "age.value.error.old" , "age > 100");
        }
    }
}

测试代码:

public class TestPerson {
    public static void main(String[] args) {
        Person person = new Person();
        // DataBinder作用为数据校验,结合Validator接口对绑定的数据进行校验
        DataBinder dataBinder = new DataBinder(person);
        dataBinder.setValidator(new PersonValidator());
        dataBinder.validate();

        BindingResult bindingResult = dataBinder.getBindingResult();
        System.out.println(bindingResult.getAllErrors());
    }
}

2. Bean Validation注解实现

User:

public class User {
    @NotNull
    public String name;
    @Min(0)
    @Max(99)
    public 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;
    }
}

配置类:

@Configuration
@ComponentScan("com.guang.validator.two")
public class ValidationConfig {

    @Bean
    // @Bean注解用于将一个方法的返回值注册为Spring应用程序上下文中的一个Bean,帮助开发者显示的定义和创建bean
    public LocalValidatorFactoryBean validator(){
        return new LocalValidatorFactoryBean();
    }
}
  1. import jakarta.validation.Validator
@Component
public class MyValidation1 {
    @Autowired
    // LocalValidatorFactoryBean是Validator的实现类
    private Validator validator;

    public boolean validatorByUserOne(User user){
        Set<ConstraintViolation<User>> validate = validator.validate(user);
        return validate.isEmpty();
    }
}
  1. import org.springframework.validation.Validator
@Component
public class MyValidation2 {
    @Autowired
    private Validator validator;

    public boolean validatorByUserTwo(User user){
        BindException bindException = new BindException(user, user.getName());
        validator.validate(user, bindException);
        return bindException.hasErrors();
    }
}

3. 基于方法实现校验

4.自定义校验

8. 提前编译

补充

1. 反射

  1. 获取class对象
public class Car {

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

    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;
    }

    public String getName() {
        return name;
    }

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

    public Car() {
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }

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

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

public class TestCar {
    // 1.获取class对象多种方式
    @Test
    public void test() throws Exception {
        // 1.类名.class
        Class clazz1 = Car.class;
        // 2.对象.getClass()
        Class clazz2 = new Car().getClass();
        // 3.Class.forName("全路径")
        Class clazz3 = Class.forName("com.guang.reflect.Car");
    }
}
  1. 获取构造方法
public void test1() throws Exception{
        Class<Car> clazz = Car.class;
        // getConstructors()获取所有public构造方法
        // getDeclaredConstructors()获取所有构造方法
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
//            System.out.println(constructor);
            // 获取构造器名字
//            System.out.println(constructor.getName());
        }

        //指定有参构造,同时其为public
        //getDeclaredConstructor()泛型class对象且为可变参数
        Car car = clazz.getConstructor(String.class, int.class, String.class).newInstance("BMW", 10, "white");
        System.out.println(car);

         Constructor c = clazz.getDeclaredConstructor(String.class, int.class, String.class);
         c.setAccessible(true);
        Car car1 = (Car) c.newInstance("BMW", 10, "white");
        System.out.println(car1);
    }
  1. 获取属性
public void test2() throws Exception{
        Class<Car> clazz = Car.class;
        Car car = clazz.getConstructor().newInstance();
        //获取所有Public属性
        Field[] fields = clazz.getFields();

        //获取所有属性
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.getName().equals("name")) {
                field.setAccessible(true);
                field.set(car, "五菱宏光");
            }
//            System.out.println(field.getName()+field.getType());
        }
        System.out.println(car);
}
  1. 获取方法
public void test3() throws Exception{
        Class<?> aClass = Class.forName("com.guang.reflect.Car");
        Constructor<?> c = aClass.getDeclaredConstructor(String.class, int.class, String.class);
        c.setAccessible(true);
        Car car = (Car) c.newInstance("BMW", 10, "red");

        // public method
        Method[] method = aClass.getMethods();
        for (Method m : method) {
            if (m.getName().equals("toString")){
                String info = (String) m.invoke(car);
                System.out.println(info);
            }
//            System.out.println(m.getName());
        }
        // private method
        Method[] method1 = aClass.getMethods();
        for (Method m : method1) {
            if (m.getName().equals("toString")){
                m.setAccessible(true);
                String info = (String) m.invoke(car);
                System.out.println(info);
            }
//            System.out.println(m.getName());
}

2. 手写IoC容器

  1. 定义UserDao、UserService接口及其实现类
public interface UserDao {
}

@Bean
public class UserDaoImpl implements  UserDao{
    @Di
    public UserDao userDaoImpl;
}

public interface UserService {
}

@Bean
public class UserServiceImpl implements  UserService{
}
  1. 自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
  1. 定义ApplicationContext接口及AnnotationApplicationContext实现类
public interface ApplicationContext {
    Object getBean(Class<?> clazz);
}
public class AnnotationApplicationContext implements ApplicationContext{
	// map集合存放键值对,key为Class对象,若有接口则接口Class对象,反之则自身
    private Map<Class, Object> beanFactory = new HashMap<Class, Object>();
    //定义“根目录”,便于后续截取字符串
    public static String rootPath;

    @Override
    public Object getBean(Class<?> clazz) {
        // 根据key返回value,即创建的对象
        return beanFactory.get(clazz);
    }

    //创建有参数构造,传递包路径,设置包扫描规则。具体来说,扫描当前包及其子包,将@Bean类创建实例,并将@Di属性注入值
    public AnnotationApplicationContext(String basePackage){
        // 例如basePackage = com.guang, 替换为com\guang
        //其中第一个参数是正则表达式,第二个参数则作为正常字符串来处理
        String packagePath = basePackage.replaceAll("\\.","\\\\");
        try {
            //获取相关路径
            Enumeration<URL> 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() - basePackage.length());
                loadBean(new File(filePath));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public void loadBean(File fileParent){
        // 判断是否为文件夹
        if(fileParent.isDirectory()){
            // 返回文件夹中内容
            File[] childrenFiles  = fileParent.listFiles();
            //若打开失败或文件夹中无子文件则return
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            //若子文件为文件夹递归调用loadBean()
            for (File child : childrenFiles) {
                if(child.isDirectory()){
                    loadBean(child);
                }else{
                    //获取包路径
                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //判断是否为.class文件
                    if(pathWithClass.endsWith(".class")){
                        String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                        try {
                            //反射获得Class对象
                            Class<?> aClass = Class.forName(fullName);
                            //判断是否为接口,是接口则无需实例化
                            if(!aClass.isInterface()){
                                //判断是否有@Bean注解
                                Bean annotation = aClass.getAnnotation(Bean.class);
                                if(annotation != null){
                                    //若有@Bean则实例化
                                    Object instance = aClass.newInstance();
                                    //若其为接口实现类,则在beanFactory中key为接口名,反之为自身名字
                                    if(aClass.getInterfaces().length > 0){
                                        System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());
                                        beanFactory.put(aClass.getInterfaces()[0],instance);
                                    }else{
                                        System.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());
                                        beanFactory.put(aClass, instance);
                                    }


                                }
                            }
                        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        }
        loadDi();
    }

    //注入属性
    public void loadDi(){
        for(Map.Entry<Class, Object> entry : beanFactory.entrySet()){
            Object obj = entry.getValue();
            Class<?> aClass = obj.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for(Field field : declaredFields){
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null){
                    field.setAccessible(true);
                    System.out.println("正在给【"+obj.getClass().getName()+"】属性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");
                    try {
                        field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}
  1. 测试
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationApplicationContext("com.guang");
        UserDao userDao = (UserDao)context.getBean(UserDao.class);
        System.out.println(userDao);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值