目录
一、程序入门
(一)、入门案例
1、创建父工程
2、创建子工程
3、在子工程的pom文件中引入spring核心依赖
4、在子工程中创建实体类
5、在resources中添加配置文件(.xml)
6、编写测试类
7、spring中用反射创建实体类
(二)、启用Log4j2日志框架
1、引入依赖
2、在类的根路径下提供log4j2.xml配置文件
3、手动写日志
二、容器IoC
(一)、基于xml文件进行bean管理
1、xml文件中创建对象
2、获取对象
3、依赖注入
(1)、基于set方法
(2)、基于构造器完成
(3)、特殊值处理
1)、空值处理(null)
2)、xml实体
3)、CDATA节
(4)对象类型属性注入
1)、引用外部bean
2)、内部bean
3)、级联属性赋值
(5)、数组类型注入
(6)、集合类型注入
1)、List
2)、Map
3)、引用集合类型的bean
(7)、p命名空间注入
(8)、引入外部属性文件
1)、引入相关依赖
2)、在resources下写相关的配置文件
3)、在xml文件中进行依赖注入
4、bean的作用域
5、bean的生命周期
(1)、基本生命周期过程
(2)、配置bean
(3)、测试
6、FactoryBean
7、基于xml自动装配(autowire)
(1)、创建实体类
(2)、配置bean
(3)、测试
(二)、基于注解管理bean
1、开启组件扫描
(1)、最基本扫描方式
(2)、排除指定组件
(3) 、只扫描指定组件
2、使用注解定义 Bean
3、@Autowired注入(默认按类型注入)
(1)、属性注入
(2)、set注入
(3)、构造方法注入
(4)、形参上注入
(5)、只有一个构造函数,无注解
(6)、@Autowired注解和@Qualifier注解联合
4、@Resource注入(默认根据名称自动装配)
(1)、根据name注入
(2)、name未知注
(3)、其他情况
@Resource注解:默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个
(三)、Spring全注解开发
1、配置类
2、测试类
三、原理-手写IoC
(一)、回顾java反射
(二)、实现spring的IoC
1、准备测试用的bean
2、定义注解
3、编写逻辑
4、测试
四、面向切面: AOP
(1)、动态代理
(二)、基于注解的AOP
1、引入依赖
2、准备被代理的目标资源
3、在Spring的配置文件中配置
4、创建切面类
5、切入点表达式
6、重用切入点表达式
7、切面的优先级
(三)、基于xml的AOP
1、把切面类中的注解删除
2、配置文件
五、单元测试: Junit
(一)、整合Junit5
1、引入依赖
2、添加配置文件
3、测试
(二)、整合Junit4
1、添加依赖
2、测试
六、声明式事务
(一)、基于注解的声明式事务
1、配置文件
2、添加注解@Transactional
3、事务属性
(1)、只读
(2)、超时
(3)、回滚策略
(4)、隔离级别
(5)、传播行为
4、全注解配置事务
(二)、基于XML的声明式事务
七、资源操作:Resources
(一)、Resource的实现类
1、UrlResource访问网络资源
2、ClassPathResource 访问类路径下资源
3、FileSystemResource 访问文件系统资源
(二)、ResourceLoader 接口
(三)、ResourceLoaderAware 接口
(四)、使用Resource 作为属性
(五)、应用程序上下文和资源路径
八、数据校验
(一)、通过Validator接口实现
1、引入依赖
2、创建实体类
3、创建类实现Validator接口,实现接口方法指定校验规则
4、使用上述Validator进行测试
(二)、Bean Validation注解实现
1、创建配置类
2、创建实体类,使用注解定义校验规则
3、使用两种不同的校验器实现
(1)使用jakarta.validation.Validator校验
(2)使用org.springframework.validation.Validator校验
4、 测试
(三)、基于方法实现校验
1、 创建配置类,配置MethodValidationPostProcessor
2、 创建实体类,使用注解设置校验规则
3、 定义Service类,通过注解操作对象
4、测试
(四)、实现自定义校验
1、自定义校验注解
2、编写真正的校验类
org.springframework
spring-context
6.0.2
org.junit.jupiter
junit-jupiter-api
5.3.1
public class User {
public void add(){
System.out.println("add......");
}
}
bean.xml
@Test
public void testUserObject(){
//加载spring配置文件,对象创建
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
//获取创建的对象
User user = (User)context.getBean("user");
System.out.println(user);
//使用对象方法
user.add();
}
@Test
public void testUserObject1() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取class对象
Class clazz = Class.forName("com.atguigu.spring.User");
//调用方法创建对象
//Object o = clazz.newInstance(); 过时了
User user = (User) clazz.getDeclaredConstructor().newInstance();
user.add();
}
org.apache.logging.log4j
log4j-core
2.19.0
org.apache.logging.log4j
log4j-slf4j2-impl
2.19.0
文件名固定为:log4j2.xml,文件必须放到类根路径下。
public class TestUser {
private Logger logger = LoggerFactory.getLogger(TestUser.class);
@Test
public void testUserObject(){
//加载spring配置文件,对象创建
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
//获取创建的对象
User user = (User)context.getBean("user");
System.out.println(user);
//使用对象方法
user.add();
//手动写日志
logger.info("### 执行调用成功了...");
}
}
package com.atguigu.spring.iocxml;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testUser {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
//1、根据id获取bean
User user1 = (User)context.getBean("user");
System.out.println(user1);
//2、根据类型获取bean(要求IoC容器中该实例的bean只能有一个)
User user2 = (User)context.getBean(User.class);
System.out.println(user2);
//根据id和类型获取bean
User user3 = (User)context.getBean("user",User.class);
System.out.println(user2);
}
}
类中有属性,创建对象过程中,像属性设置值
新建book类,添加set方法
package com.atguigu.spring.iocxml.di;
public class Book {
private String bname;
private String author;
//生成set方法
public void setBname(String bname) {
this.bname = bname;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"bname='" + bname + '\'' +
", author='" + author + '\'' +
'}';
}
}
bean.xml文件中进行配置
测试
package com.atguigu.spring.iocxml.di;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testBook {
@Test
public void testSetter(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean-di.xml");
//1、根据id获取bean
Book book = (Book)context.getBean("book");
System.out.println(book);
}
}
类中添加构造方法
package com.atguigu.spring.iocxml.di;
public class Book {
private String bname;
private String author;
//有参无参构造
public Book(){
System.out.println("无参构造执行了...");
}
public Book(String bname, String author) {
this.bname = bname;
this.author = author;
System.out.println("有参构造执行了...");
}
@Override
public String toString() {
return "Book{" +
"bname='" + bname + '\'' +
", author='" + author + '\'' +
'}';
}
}
配置文件中设置
测试
package com.atguigu.spring.iocxml.di;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testBook {
@Test
public void testConstructor(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean-di.xml");
//1、根据id获取bean
Book book = (Book)context.getBean("bookCon",Book.class);
System.out.println(book);
}
}
新建类
package com.atguigu.spring.iocxml.ditest;
public class Dept {
private String dname;
public void info(){
System.out.println(dname);
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
}
package com.atguigu.spring.iocxml.ditest;
public class Emp {
private String ename;
private Integer age;
//员工属于哪个部门
private Dept dept;
public void work(){
System.out.println(ename + " emp work... " + age);
dept.info();
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
package com.atguigu.spring.iocxml.ditest;
public class Emp {
private String ename;
private Integer age;
//爱好
private String[] loves;
//员工属于哪个部门
private Dept dept;
public void work(){
System.out.println(ename + " emp work... " + age);
dept.info();
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String[] getLoves() {
return loves;
}
public void setLoves(String[] loves) {
this.loves = loves;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
吃饭
睡觉
写代码
package com.atguigu.spring.iocxml.ditest;
import java.util.List;
public class Dept {
private String dname;
//部门中有哪些部门
private List empList;
public void info(){
System.out.println(dname);
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public List getEmpList() {
return empList;
}
public void setEmpList(List empList) {
this.empList = empList;
}
}
10010
10086
抽烟
喝酒
烫头
注意:使用util:list、util:map标签必须引入相应的命名空间
以数据库为例
mysql
mysql-connector-java
8.0.30
com.alibaba
druid
1.2.15
jdbc.properties
jdbc.user=root
jdbc.password=atguigu
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver
@Test
public void testBeanScope(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-scope.xml");
User user1 = ac.getBean(User.class);
User user2 = ac.getBean(User.class);
//如果scope="prototype",返回false。 如果scope="singleton",返回true。
System.out.println(user1==user2);
}
bean对象创建(调用无参构造器)
给bean对象设置属性
bean的后置处理器(初始化之前)
bean对象初始化(需在配置bean时指定初始化方法)
bean的后置处理器(初始化之后)
bean对象就绪可以使用
bean对象销毁(需在配置bean时指定销毁方法)
IOC容器关闭
@Test
public void testLife(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-lifecycle.xml");
User bean = ac.getBean(User.class);
System.out.println("生命周期:4、通过IOC容器获取bean并使用");
ac.close(); //调用销毁方法
}
利用泛型特点,虽然配置时不时User类,但生成的bean时User类型的
package com.atguigu.spring6.bean;
public class UserFactoryBean implements FactoryBean {
@Override
public User getObject() throws Exception {
return new User();
}
@Override
public Class> getObjectType() {
return User.class;
}
}
对应的实体类中要自动装配的属性要有setter方法
Controller层
package com.atguigu.spring6.autowire.controller
public class UserController {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void saveUser(){
userService.saveUser();
}
}
Service层
package com.atguigu.spring6.autowire.service
public interface UserService {
void saveUser();
}
package com.atguigu.spring6.autowire.service.impl
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void saveUser() {
userDao.saveUser();
}
}
Dao层
package com.atguigu.spring6.autowire.dao
public interface UserDao {
void saveUser();
}
package com.atguigu.spring6.autowire.dao.impl
public class UserDaoImpl implements UserDao {
@Override
public void saveUser() {
System.out.println("保存成功");
}
}
autowire="byType" : 按类型自动装配
autowire="byName" : 按名称自动装配(对象名与id相同)
@Test
public void testAutoWireByXML(){
ApplicationContext ac = new ClassPathXmlApplicationContext("autowire-xml.xml");
UserController userController = ac.getBean(UserController.class);
userController.saveUser();
}
package com.atguigu.spring6.controller;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private UserService userService;
public void out() {
userService.out();
System.out.println("Controller层执行结束。");
}
}
效果与第一种相似,但能在set方法中写一些其他的逻辑
package com.atguigu.spring6.service.impl;
import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service层执行结束");
}
}
package com.atguigu.spring6.service.impl;
import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service层执行结束");
}
}
构造方法的形参上架@Autowired
package com.atguigu.spring6.service.impl;
import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(@Autowired UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service层执行结束");
}
}
当有参数的构造方法只有一个时,@Autowired注解可以省略。
说明:有多个构造方法时呢?大家可以测试(再添加一个无参构造函数),测试报错
新版6.0.7本可以一个有参一个无参
package com.atguigu.spring6.service.impl;
import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void out() {
userDao.print();
System.out.println("Service层执行结束");
}
}
@Qualifier("") 根据名称注入
package com.atguigu.spring6.service.impl;
import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDaoImpl") // 指定bean的名字
private UserDao userDao;
@Override
public void out() {
userDao.print();
System.out.println("Service层执行结束");
}
}
@Resource注解属于JDK扩展包,所以不在JDK当中,需要额外引入以下依赖:【如果是JDK8的话不需要额外引入依赖。高于JDK11或低于JDK8需要引入以下依赖。】
jakarta.annotation
jakarta.annotation-api
2.1.1
package com.atguigu.spring6.dao.impl;
import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository("myUserDao")
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao层执行结束");
}
}
package com.atguigu.spring6.service.impl;
import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Resource(name = "myUserDao")
private UserDao myUserDao;
@Override
public void out() {
myUserDao.print();
System.out.println("Service层执行结束");
}
}
package com.atguigu.spring6.dao.impl;
import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;
@Repository("myUserDao")
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao层执行结束");
}
}
package com.atguigu.spring6.service.impl;
import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao myUserDao;
@Override
public void out() {
myUserDao.print();
System.out.println("Service层执行结束");
}
}
全注解开发就是不再使用spring配置文件(.xml)了,写一个配置类来代替配置文件。
@Configuration //配置类的注解
@ComponentScan("com.atguigu.spring6") //设置组件扫描范围
package com.atguigu.spring6.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//@ComponentScan({"com.atguigu.spring6.controller", "com.atguigu.spring6.service","com.atguigu.spring6.dao"})
@ComponentScan("com.atguigu.spring6") //设置组件扫描范围
public class Spring6Config {
}
@Test
public void testAllAnnotation(){
ApplicationContext context = new
AnnotationConfigApplicationContext(Spring6Config.class);
UserController userController = context.getBean("userController", UserController.class);
userController.out();
logger.info("执行成功");
}
Spring框架的IOC是基于Java反射机制实现的
创建类
package com.atguigu.reflect;
public class Car {
private String name;
private int age;
private String color;
public Car(){}
public Car(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
private void run(){
System.out.println("私有方法....");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
package com.atguigu.reflect;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestCar {
//1、获取Class对象多种方式
@Test
public void test01() throws Exception {
//1 类名.class
Class clazz1 = Car.class;
//2 对象.getClass()
Class clazz2 = new Car().getClass();
//3 Class.forName("全路径")
Class clazz3 = Class.forName("com.atguigu.reflect.Car");
//实例化
Car car = (Car)clazz3.getConstructor().newInstance();
System.out.println(car);
}
//2、获取构造方法
@Test
public void test02() throws Exception {
Class clazz = Car.class;
//获取所有构造
// getConstructors()获取所有public的构造方法
// Constructor[] constructors = clazz.getConstructors();
// getDeclaredConstructors()获取所有的构造方法public private
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor c:constructors) {
System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount());
}
//指定有参数构造创建对象
//1 构造public
// Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
// Car car1 = (Car)c1.newInstance("夏利", 10, "红色");
// System.out.println(car1);
//2 构造private
Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
c2.setAccessible(true);
Car car2 = (Car)c2.newInstance("捷达", 15, "白色");
System.out.println(car2);
}
//3、获取属性
@Test
public void test03() throws Exception {
Class clazz = Car.class;
Car car = (Car)clazz.getDeclaredConstructor().newInstance();
//获取所有public属性
//Field[] fields = clazz.getFields();
//获取所有属性(包含私有属性)
Field[] fields = clazz.getDeclaredFields();
for (Field field:fields) {
if(field.getName().equals("name")) {
//设置允许访问
field.setAccessible(true);
field.set(car,"五菱宏光");
System.out.println(car);
}
System.out.println(field.getName());
}
}
//4、获取方法
@Test
public void test04() throws Exception {
Car car = new Car("奔驰",10,"黑色");
Class clazz = car.getClass();
//1 public方法
Method[] methods = clazz.getMethods();
for (Method m1:methods) {
//System.out.println(m1.getName());
//执行方法 toString
if(m1.getName().equals("toString")) {
String invoke = (String)m1.invoke(car);
//System.out.println("toString执行了:"+invoke);
}
}
//2 private方法
Method[] methodsAll = clazz.getDeclaredMethods();
for (Method m:methodsAll) {
//执行方法 run
if(m.getName().equals("run")) {
m.setAccessible(true);
m.invoke(car);
}
}
}
}
package com.atguigu.spring6.test.dao;
public interface UserDao {
public void print();
}
package com.atguigu.spring6.test.dao.impl;
import com.atguigu.spring.dao.UserDao;
public class UserDaoImpl implements UserDao {
@Override
public void print() {
System.out.println("Dao层执行结束");
}
}
package com.atguigu.spring6.test.service;
public interface UserService {
public void out();
}
package com.atguigu.spring.test.service.impl;
import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.service.UserService;
@Bean
public class UserServiceImpl implements UserService {
// private UserDao userDao;
@Override
public void out() {
//userDao.print();
System.out.println("Service层执行结束");
}
}
@bean
package com.atguigu.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
@Di
package com.atguigu.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
package com.atguigu.bean;
public interface ApplicationContext {
Object getBean(Class clazz);
}
package com.atguigu.bean;
import com.atguigu.anno.Bean;
import com.atguigu.anno.Di;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class AnnotationApplicationContext implements ApplicationContext {
//创建map集合,放bean对象
private Map beanFactory = new HashMap<>();
private static String rootPath;
@Override
public Object getBean(Class clazz) {
return beanFactory.get(clazz);
}
//设置包的扫描规则
public AnnotationApplicationContext(String basePackage) {
try {
//1、把.替换成/
String packagePath = basePackage.replaceAll("\\.", "\\\\");
//2、获得包的绝对路径
Enumeration urls
= Thread.currentThread()
.getContextClassLoader()
.getResources(packagePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
String filePath = URLDecoder.decode(url.getFile(), "utf-8");
//获取包前面路径部分,字符串截取
rootPath = filePath.substring(0, filePath.length() - packagePath.length());
//包扫描
loadBean(new File(filePath));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
//属性注入
loadDi();
}
private void loadBean(File file) throws Exception {
//1、判断当前是否是文件夹
if (file.isDirectory()) {
//2.获取文件夹中的所有文件
File[] childrenFiles = file.listFiles();
//3.判断文件夹里是否为空
if (childrenFiles == null || childrenFiles.length == 0) {
return;
}
//4.文件夹里不为空,遍历文件夹所有内容
for (File child : childrenFiles) {
//4.1遍历每个File对象,继续判断,如果还是文件夹,递归
if (child.isDirectory()) {
loadBean(child);
} else {
//4.2 遍历的File是文件
//4.3 得到包路径+类名称部分
String pathWithClass =
child.getAbsolutePath()
.substring(rootPath.length() - 1);
//4.4判断当前文件类型是否是 .class
if (pathWithClass.endsWith(".class")) {
//4.5 如果是.class类型,把路径\替换成. 把.class去掉
String allName = pathWithClass.replaceAll("\\\\", ".")
.replace(".class", "");
//4.6 判断类上面是否有注解@Bean,如果有实例化过程
//4.6.1 获取类的Class
Class> clazz = Class.forName(allName);
//4.6.2 判断是不是接口,把非接口类实例化对象放在map中
if (!clazz.isInterface()) {
//4.6.3 判断类上面是否有注解 @Bean
Bean annotation = clazz.getAnnotation(Bean.class);
if (annotation != null) {
//4.6.4 实例化
Object instance = clazz.getConstructor().newInstance();
//4.7 把实例化对象放入到map集合中
//4.7.1 判断当前类是否有接口,让接口class作为map的key
if (clazz.getInterfaces().length > 0) {
beanFactory.put(clazz.getInterfaces()[0],instance);
} else {
beanFactory.put(clazz,instance);
}
}
}
}
}
}
}
}
private void loadDi() {
//1 遍历beanFactory的map集合
Set> entries = beanFactory.entrySet();
for (Map.Entry entry : entries) {
//2 获得map集合每个对象的value,每个对象属性获取到
Object obj = entry.getValue();
//获取对象class
Class> clazz = obj.getClass();
//获取对象的属性
Field[] declaredFields = clazz.getDeclaredFields();
//3 遍历每个对象属性数组,得到每个属性
for (Field field : declaredFields) {
//4 判断属性上是否有@Di注解
Di annotation = field.getAnnotation(Di.class);
if(annotation != null){
//如果是私有属性,设置可以设置值
field.setAccessible(true);
//5 如果有@Di注解,把对象注入
try {
field.set(obj,beanFactory.get(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
package com.atguigu;
import com.atguigu.bean.AnnotationApplicationContext;
import com.atguigu.bean.ApplicationContext;
import com.atguigu.service.UserService;
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new AnnotationApplicationContext("com.atguigu");
UserService userService = (UserService)context.getBean(UserService.class);
System.out.println(userService);
userService.add();
}
}
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
/**
* newProxyInstance():创建一个代理实例
* 其中有三个参数:
* 1、classLoader:加载动态生成的代理类的类加载器
* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class>[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* proxy:代理对象
* method:代理对象需要实现的方法,即其中需要重写的方法
* args:method所对应方法的参数
*/
Object result = null;
try {
System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
result = method.invoke(target, args);
System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
} finally {
System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
org.springframework
spring-aop
6.0.2
org.springframework
spring-aspects
6.0.2
package com.atguigu.spring.aop.annoaop;
public interface Calculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
package com.atguigu.spring.aop.annoaop;
public class CalculatorImpl implements Calculator {
@Override
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部 result = " + result);
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部 result = " + result);
return result;
}
}
package com.atguigu.spring.aop.annoaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
//切面类
@Aspect //表示是一个切面类
@Component //交给spring的ioc容器管理
public class LogAspect {
//前置通知
@Before("execution(public int com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName(); //方法名
String args = Arrays.toString(joinPoint.getArgs()); //参数
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
//后置通知(在返回之后执行,类似于try-catch-finally 中的 finally)
// @After("execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
// @After(value = "com.atguigu.spring.aop.annoaop.LogAspect.pointCut()")
@After(value = "pointCut()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
}
//返回通知(在返回之前执行,可以得到目标函数的返回值)
@AfterReturning(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}
//异常通知(目标方法出现异常时执行)
@AfterThrowing(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}
//环绕通知
@Around("execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
//类似前置通知
System.out.println("环绕通知-->目标对象方法执行之前");
//目标对象(连接点)方法的执行
result = joinPoint.proceed();
//类似返回通知
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) { //类似异常通知
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally { //类似后置通知
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}
//重用切入点表达式
@Pointcut(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
public void pointCut(){}
}
//重用切入点表达式
@Pointcut(value = "execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
public void pointCut(){}
//后置通知(在返回之后执行,类似于try-catch-finally 中的 finally)
// @After("execution(* com.atguigu.spring.aop.annoaop.CalculatorImpl.*(..))")
// @After(value = "com.atguigu.spring.aop.annoaop.LogAspect.pointCut()")
@After(value = "pointCut()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
}
相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。
优先级高的切面:外面
优先级低的切面:里面
使用@Order注解(在切面类上加)可以控制切面的优先级:
package com.atguigu.spring.aop.xmlaop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
//切面类
@Component //交给spring的ioc容器管理
public class LogAspect {
//前置通知
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName(); //方法名
String args = Arrays.toString(joinPoint.getArgs()); //参数
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
//后置通知
@After(value = "pointCut()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
}
//返回通知
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}
//异常通知
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}
//环绕通知
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
//类似前置通知
System.out.println("环绕通知-->目标对象方法执行之前");
//目标对象(连接点)方法的执行
result = joinPoint.proceed();
//类似返回通知
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) { //类似异常通知
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally { //类似后置通知
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}
}
org.springframework
spring-test
6.0.2
org.junit.jupiter
junit-jupiter-api
5.9.0
package com.atguigu.spring.junit.junit5;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
//第一种方式
//@ExtendWith(SpringExtension.class)
//@ContextConfiguration("classpath:bean.xml")
//第二种方式
@SpringJUnitConfig(locations = "classpath:bean.xml")
public class SpringTestJunit5 {
@Autowired
private User user;
@Test
public void testUse(){
user.run();
}
}
junit
junit
4.12
import com.atguigu.spring6.bean.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:beans.xml")
public class SpringJUnit4Test {
@Autowired
private User user;
@Test
public void testUser(){
System.out.println(user);
}
}
在spring配置文件中引入tx命名空间
在Spring的配置文件中添加配置:
@Transactional标识在方法上,则只会影响该方法
@Transactional标识的类上,则会影响类中所有的方法
只能进行查询操作
@Transactional(readOnly = true)
默认值为-1永不超时
//超时时间单位秒
@Transactional(timeout = 3)
声明式事务默认只针对运行时异常回滚,编译时异常不回滚。
可以通过@Transactional中相关属性设置回滚策略
rollbackFor属性:需要设置一个Class类型的对象
rollbackForClassName属性:需要设置一个字符串类型的全类名
noRollbackFor属性:需要设置一个Class类型的对象
rollbackFor属性:需要设置一个字符串类型的全类名
@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) {
//查询图书的价格
Integer price = bookDao.getPriceByBookId(bookId);
//更新图书的库存
bookDao.updateStock(bookId);
//更新用户的余额
bookDao.updateBalance(userId, price);
System.out.println(1/0);
}
java程序中仍然抛出了异常,但是数据库不回滚
@Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化
有A、B两个方法且方法上都有事务注解,A中连续两次调用B ,第一次成功,第二次失败。
@Transactional(propagation = Propagation.REQUIRED),默认情况,表示如果当前线程上有已经开启的事务可用,那么就在这个事务中运行 :
B上的注解为@Transactional(propagation = Propagation.REQUIRED),因为式A调用B,所以线程上已经有A的事务,所以直接执行A的事务,两次调用都回滚。
@Transactional(propagation = Propagation.REQUIRES_NEW),表示不管当前线程上是否有已经开启的事务,都要开启新事务:
B上的注解为@Transactional(propagation = Propagation.REQUIRED),不管当前线程上是否有已经开启的事务,都要开启新事务,每调用都是在B的事务中执行,所以第一次成功(不回滚),第二失败(回滚)
package com.atguigu.spring6.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan("com.atguigu.spring6")
@EnableTransactionManagement
public class SpringConfig {
@Bean
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring?characterEncoding=utf8&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
测试:
import com.atguigu.spring6.config.SpringConfig;
import com.atguigu.spring6.controller.BookController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
public class TxByAllAnnotationTest {
@Test
public void testTxAllAnnotation(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
BookController accountService = applicationContext.getBean("bookController", BookController.class);
accountService.buyBook(1, 1);
}
}
将Spring配置文件中去掉tx:annotation-driven 标签,并添加配置:
Spring 的 Resource 接口位于 org.springframework.core.io 中。 旨在成为一个更强大的接口,用于抽象对低级资源的访问。以下显示了Resource接口定义的方法
Resource的一个实现类,用来访问网络资源,它支持URL的绝对路径。
http:------该前缀用于访问基于HTTP协议的网络资源。
ftp:------该前缀用于访问基于FTP协议的网络资源
file: ------该前缀用于从文件系统中读取资源
实验:访问基于HTTP协议的网络资源
package com.atguigu.spring6.resources;
import org.springframework.core.io.UrlResource;
public class UrlResourceDemo {
public static void loadAndReadUrlResource(String path){
// 创建一个 Resource 对象
UrlResource url = null;
try {
url = new UrlResource(path);
// 获取资源名
System.out.println(url.getFilename());
System.out.println(url.getURI());
// 获取资源描述
System.out.println(url.getDescription());
//获取资源内容
System.out.println(url.getInputStream().read());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
//访问网络资源
loadAndReadUrlResource("http://www.baidu.com");
}
}
实验二:在项目根路径下创建文件,从文件系统中读取资源
方法不变,修改调用传递路径
public static void main(String[] args) {
//1 访问网络资源
//loadAndReadUrlResource("http://www.atguigu.com");
//2 访问文件系统资源
loadAndReadUrlResource("file:atguigu.txt");
}
ClassPathResource 用来访问类加载路径下的资源,相对于其他的 Resource 实现类,其主要优势是方便访问类加载路径里的资源,尤其对于 Web 应用,ClassPathResource 可自动搜索位于 classes 下的资源文件,无须使用绝对路径访问。
实验:在类路径下创建文件atguigu.txt,使用ClassPathResource 访问
package com.atguigu.spring6.resources;
import org.springframework.core.io.ClassPathResource;
import java.io.InputStream;
public class ClassPathResourceDemo {
public static void loadAndReadUrlResource(String path) throws Exception{
// 创建一个 Resource 对象
ClassPathResource resource = new ClassPathResource(path);
// 获取文件名
System.out.println("resource.getFileName = " + resource.getFilename());
// 获取文件描述
System.out.println("resource.getDescription = "+ resource.getDescription());
//获取文件内容
InputStream in = resource.getInputStream();
byte[] b = new byte[1024];
while(in.read(b)!=-1) {
System.out.println(new String(b));
}
}
public static void main(String[] args) throws Exception {
loadAndReadUrlResource("atguigu.txt");
}
}
Spring 提供的 FileSystemResource 类用于访问文件系统资源,使用 FileSystemResource 来访问文件系统资源并没有太大的优势,因为 Java 提供的 File 类也可用于访问文件系统资源。
实验:使用FileSystemResource 访问文件系统资源
package com.atguigu.spring6.resources;
import org.springframework.core.io.FileSystemResource;
import java.io.InputStream;
public class FileSystemResourceDemo {
public static void loadAndReadUrlResource(String path) throws Exception{
//相对路径
FileSystemResource resource = new FileSystemResource("atguigu.txt");
//绝对路径
//FileSystemResource resource = new FileSystemResource("C:\\atguigu.txt");
// 获取文件名
System.out.println("resource.getFileName = " + resource.getFilename());
// 获取文件描述
System.out.println("resource.getDescription = "+ resource.getDescription());
//获取文件内容
InputStream in = resource.getInputStream();
byte[] b = new byte[1024];
while(in.read(b)!=-1) {
System.out.println(new String(b));
}
}
public static void main(String[] args) throws Exception {
loadAndReadUrlResource("atguigu.txt");
}
}
实验一:ClassPathXmlApplicationContext获取Resource实例
package com.atguigu.spring6.resouceloader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
public class Demo1 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext();
// 通过ApplicationContext访问资源
// ApplicationContext实例获取Resource实例时,
// 默认采用与ApplicationContext相同的资源访问策略
Resource res = ctx.getResource("atguigu.txt");
System.out.println(res.getFilename());
}
}
实验二:FileSystemApplicationContext获取Resource实例
package com.atguigu.spring6.resouceloader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;
public class Demo2 {
public static void main(String[] args) {
ApplicationContext ctx = new FileSystemXmlApplicationContext();
Resource res = ctx.getResource("atguigu.txt");
System.out.println(res.getFilename());
}
}
Spring将采用和ApplicationContext相同的策略来访问资源。也就是说,如果ApplicationContext是FileSystemXmlApplicationContext,res就是FileSystemResource实例;如果ApplicationContext是ClassPathXmlApplicationContext,res就是ClassPathResource实例
当Spring应用需要进行资源访问时,实际上并不需要直接使用Resource实现类,而是调用ResourceLoader实例的getResource()方法来获得资源,ReosurceLoader将会负责选择Reosurce实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来
另外,使用ApplicationContext访问资源时,可通过不同前缀指定强制使用指定的ClassPathResource、FileSystemResource等实现类
Resource res = ctx.getResource("calsspath:bean.xml");
Resrouce res = ctx.getResource("file:bean.xml");
Resource res = ctx.getResource("http://localhost:8080/beans.xml");
ResourceLoaderAware接口实现类的实例将获得一个ResourceLoader的引用,ResourceLoaderAware接口也提供了一个setResourceLoader()方法,该方法将由Spring容器负责调用,Spring容器会将一个ResourceLoader对象作为该方法的参数传入。
如果把实现ResourceLoaderAware接口的Bean类部署在Spring容器中,Spring容器会将自身当成ResourceLoader作为setResourceLoader()方法的参数传入。由于ApplicationContext的实现类都实现了ResourceLoader接口,Spring容器自身完全可作为ResorceLoader使用。
第一步 创建类,实现ResourceLoaderAware接口
package com.atguigu.spring6.resouceloader;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
public class TestBean implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
//实现ResourceLoaderAware接口必须实现的方法
//如果把该Bean部署在Spring容器中,该方法将会有Spring容器负责调用。
//SPring容器调用该方法时,Spring会将自身作为参数传给该方法。
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
//返回ResourceLoader对象的应用
public ResourceLoader getResourceLoader(){
return this.resourceLoader;
}
}
第二步 创建bean.xml文件,配置TestBean
第三步 测试
package com.atguigu.spring6.resouceloader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
public class Demo3 {
public static void main(String[] args) {
//Spring容器会将一个ResourceLoader对象作为该方法的参数传入
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
TestBean testBean = ctx.getBean("testBean",TestBean.class);
//获取ResourceLoader对象
ResourceLoader resourceLoader = testBean.getResourceLoader();
System.out.println("Spring容器将自身注入到ResourceLoaderAware Bean 中 ? :" + (resourceLoader == ctx));
//加载其他资源
Resource resource = resourceLoader.getResource("atguigu.txt");
System.out.println(resource.getFilename());
System.out.println(resource.getDescription());
}
}
资源所在的物理位置将被耦合到代码中,如果资源位置发生改变,则必须改写程序。因此,通常建议采用第二种方法,让 Spring 为 Bean 实例依赖注入资源。
第一步 创建依赖注入类,定义属性和方法
package com.atguigu.spring6.resouceloader;
import org.springframework.core.io.Resource;
public class ResourceBean {
private Resource res;
public void setRes(Resource res) {
this.res = res;
}
public Resource getRes() {
return res;
}
public void parse(){
System.out.println(res.getFilename());
System.out.println(res.getDescription());
}
}
第二步 创建spring配置文件,配置依赖注入
第三步 测试
package com.atguigu.spring6.resouceloader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo4 {
public static void main(String[] args) {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("bean.xml");
ResourceBean resourceBean = ctx.getBean("resourceBean",ResourceBean.class);
resourceBean.parse();
}
}
实验一:classpath前缀使用
package com.atguigu.spring6.context;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;
public class Demo1 {
public static void main(String[] args) {
/*
* 通过搜索文件系统路径下的xml文件创建ApplicationContext,
* 但通过指定classpath:前缀强制搜索类加载路径
* classpath:bean.xml
* */
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath:bean.xml");
System.out.println(ctx);
Resource resource = ctx.getResource("atguigu.txt");
System.out.println(resource.getFilename());
System.out.println(resource.getDescription());
}
}
实验二:classpath通配符使用
classpath * :前缀提供了加载多个XML配置文件的能力,当使用classpath*:前缀来指定XML配置文件时,系统将搜索类加载路径,找到所有与文件名匹配的文件,分别加载文件中的配置定义,最后合并成一个ApplicationContext。
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean.xml");
System.out.println(ctx);
当使用classpath * :前缀时,Spring将会搜索类加载路径下所有满足该规则的配置文件。
如果不是采用classpath * :前缀,而是改为使用classpath:前缀,Spring则只加载第一个符合条件的XML文件
注意 :
classpath * : 前缀仅对ApplicationContext有效。实际情况是,创建ApplicationContext时,分别访问多个配置文件(通过ClassLoader的getResource方法实现)。因此,classpath * :前缀不可用于Resource。
使用三:通配符其他使用
一次性加载多个配置文件的方式:指定配置文件时使用通配符
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:bean*.xml");
Spring允许将classpath*:前缀和通配符结合使用:
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:bean*.xml");
org.hibernate.validator
hibernate-validator
7.0.5.Final
org.glassfish
jakarta.el
4.0.1
package com.atguigu.spring6.validation.method1;
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.atguigu.spring6.validation.method1;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
public class PersonValidator implements Validator {
@Override
public boolean supports(Class> clazz) {
return Person.class.equals(clazz);
}
@Override
public void validate(Object object, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
Person p = (Person) object;
if (p.getAge() < 0) {
errors.rejectValue("age", "error value < 0");
} else if (p.getAge() > 110) {
errors.rejectValue("age", "error value too old");
}
}
}
上面定义的类,其实就是实现接口中对应的方法,
supports方法用来表示此校验用在哪个类型上,
validate是设置校验逻辑的地点,其中ValidationUtils,是Spring封装的校验工具类,帮助快速实现校验。
package com.atguigu.spring6.validation.method1;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
public class TestMethod1 {
public static void main(String[] args) {
//创建person对象
Person person = new Person();
person.setName("lucy");
person.setAge(-1);
// 创建Person对应的DataBinder
DataBinder binder = new DataBinder(person);
// 设置校验
binder.setValidator(new PersonValidator());
// 由于Person对象中的属性为空,所以校验不通过
binder.validate();
//输出结果
BindingResult results = binder.getBindingResult();
System.out.println(results.getAllErrors());
}
}
@Configuration
@ComponentScan("com.atguigu.spring6.validation.method2")
public class ValidationConfig {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
}
package com.atguigu.spring6.validation.method2;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
public class User {
@NotNull
private String name;
@Min(0)
@Max(120)
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
常用注解说明
@NotNull 限制必须不为null
@NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0
@NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
package com.atguigu.spring6.validation.method2;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
public class MyService1 {
@Autowired
private Validator validator;
public boolean validator(User user){
Set> sets = validator.validate(user);
return sets.isEmpty();
}
}
package com.atguigu.spring6.validation.method2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindException;
import org.springframework.validation.Validator;
@Service
public class MyService2 {
@Autowired
private Validator validator;
public boolean validaPersonByValidator(User user) {
BindException bindException = new BindException(user, user.getName());
validator.validate(user, bindException);
return bindException.hasErrors();
}
}
package com.atguigu.spring6.validation.method2;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestMethod2 {
@Test
public void testMyService1() {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
MyService1 myService = context.getBean(MyService1.class);
User user = new User();
user.setAge(-1);
boolean validator = myService.validator(user);
System.out.println(validator);
}
@Test
public void testMyService2() {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
MyService2 myService = context.getBean(MyService2.class);
User user = new User();
user.setName("lucy");
user.setAge(130);
user.setAge(-1);
boolean validator = myService.validaPersonByValidator(user);
System.out.println(validator);
}
}
package com.atguigu.spring6.validation.method3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@Configuration
@ComponentScan("com.atguigu.spring6.validation.method3")
public class ValidationConfig {
@Bean
public MethodValidationPostProcessor validationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
package com.atguigu.spring6.validation.method3;
import jakarta.validation.constraints.*;
public class User {
@NotNull
private String name;
@Min(0)
@Max(120)
private int age;
@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
@NotBlank(message = "手机号码不能为空")
private String phone;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
package com.atguigu.spring6.validation.method3;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@Service
@Validated
public class MyService {
public String testParams(@NotNull @Valid User user) {
return user.toString();
}
}
package com.atguigu.spring6.validation.method3;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestMethod3 {
@Test
public void testMyService1() {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
MyService myService = context.getBean(MyService.class);
User user = new User();
user.setAge(-1);
myService.testParams(user);
}
}
package com.atguigu.spring6.validation.method4;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {CannotBlankValidator.class})
public @interface CannotBlank {
//默认错误消息
String message() default "不能包含空格";
//分组
Class>[] groups() default {};
//负载
Class extends Payload>[] payload() default {};
//指定多个时使用
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
CannotBlank[] value();
}
}
package com.atguigu.spring6.validation.method4;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CannotBlankValidator implements ConstraintValidator {
@Override
public void initialize(CannotBlank constraintAnnotation) {
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
//null时不进行校验
if (value != null && value.contains(" ")) {
//获取默认提示信息
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
System.out.println("default message :" + defaultConstraintMessageTemplate);
//禁用默认提示信息
context.disableDefaultConstraintViolation();
//设置提示语
context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
return false;
}
return true;
}
}