文章目录
1. 核心概念介绍
-
IoC容器
IoC(Inversion of Control)控制反转,它是一种设计思想,能够指导我们如何设计出松耦合、更优良的程序。Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。
-
DI
指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。包括set方式注入和构造器方式注入。
2. bean管理
bean管理即为Bean对象的创建,以及Bean对象中属性的赋值。
1. 基于XML方式管理bean
- 添加依赖。其中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>
-
获取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)
-
扩展知识
- 可以根据接口类型可以获取 bean 吗?
可以,前提是bean唯一。
- 如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类型可以获取 bean 吗?
不行,因为bean不唯一
-
依赖注入
- 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 < 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>-->
<!-- <!–对象类型属性注入–>-->
<!-- <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>-->
<!-- <!–对象类型属性注入–>-->
<!-- <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
- 引入依赖
<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>
-
开启组件扫描
- 基本的扫描方式
<!--开启组件扫描--> <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>
-
使用注解定义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);
}
}
-
依赖注入
- 属性注入
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();
}
}
```
- 全注解开发
使用配置类代替原有的用于开启组件扫描的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
- 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();
}
}
- 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. 基于注解方式管理
- 示例:
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);
}
}
-
事务属性
- 只可进行只读操作:
@Transactional(readOnly = true)
- 超时设置
@Transactional(timeout = 3) //单位为秒
- 回滚策略
- rollbackFor:需要设置一个class对象
- rollbackForClassName:需要设置一个字符串类型的全类名
- noRollbackFor:需要设置一个class对象
- rollbackFor:需要设置一个字符串类型的全类名
- 隔离级别
隔离级别 脏读 不可重复读 幻读 READ UNCOMMITTED 有 有 有 READ COMMITTED 无 有 有 REPEATABLE READ 无 无 有 SERIALIZABLE 无 无 无 -
传播行为
- 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注解。那么在一个事务中调用另一事务的不同行为即为不同传播行为设置。
- 全注解开发
即将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方式管理
- 示例
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接口
- 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");
}
}
- 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");
}
}
- 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确定资源访问策略
- 使用Application实现类指定访问策略
当程序创建ApplicationContext实例时,通常也是以Resource的方式来访问配置文件的,即ApplicationContext的实现类ClassPathXmlApplicationContext, FileSystemXmlApplicationContext等。
-
使用前缀指定访问策略
- classpath和classpath*区别:
classpath:只会从 单一的类路径 中加载资源文件。classpath*: 会从 所有的类路径 和 JAR 包 中查找匹配的资源文件,并且支持加载 多个匹配的文件。
-
可使用*通配符,如
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean*.xml");
-
可使用
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();
}
}
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();
}
}
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. 反射
- 获取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");
}
}
- 获取构造方法
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);
}
- 获取属性
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);
}
- 获取方法
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容器
- 定义UserDao、UserService接口及其实现类
public interface UserDao {
}
@Bean
public class UserDaoImpl implements UserDao{
@Di
public UserDao userDaoImpl;
}
public interface UserService {
}
@Bean
public class UserServiceImpl implements UserService{
}
- 自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
- 定义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);
}
}
}
}
}
}
- 测试
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);
}
}