目录
2.创建Spring容器,并指定要维护的类,提供getBean(),根据beanName获取对象
一、Spring的组件与核心
1.Bean、Context、Core三大核心组件的关系:
Bean 包装的是 Object,而 Object 必然有数据,如何给这些数据提供生存环境就是 Context要解决的问题,对 Context 来说它就是要发现每个 Bean 之间的关系,为它们建立这种关系并且要维护好这种关系。所以 Context 就是一个Bean关系的集合,这个关系集合又叫 Ioc 容器,一旦建立起这个 Ioc 容器后 Spring 就可以为你工作了。那 Core 组件又有什么用武之地呢?其实Core 就是发现、建立和维护每个 Bean 之间的关系所需要的一些类的工具,从这个角度看来,Core 这个组件叫 Util 更能让你理解。
2.Spring框架两大核心:IoC和DI
概念
IoC(Inversion of Control)简单来说就是将对象Object的创建的权力及对象的生命周期的管理过程交由Spring框架来处理,从此在开发过程中不在需要关注对象的创建和生命周期的管理,而是在需要的时候由Spring框架提供,这个由Spring框架管理对象创建和生命周期的机制称之为控制反转。
在创建对象的过程中Spring可以依据对象的关系,自动把其它对象注入(无需创建对象,直接拿着使用)进来,这个过程称之为DI(Dependency Injection)依赖注入。
总结下Spring核心就干了两件事:
1.创建对象
2.设置对象的关联关系
二、Spring的IOC
1.概述
Inversion of Control,控制反转,是指把管理对象的权利交给spring框架。
2.注解方式实现IOC
哪个类想让spring框架new,就在类上使用注解:@Component / @Service / @Controller
(1)创建类,使用注解
#Map<类名首字母要小写,类的对象> - {user=new User()}
@Component
public class User {
public void get(){
System.out.println("hello springioc");
}
}
(2)创建配置文件,只需要指定包的路径
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 包扫描:只需要指定从哪个包开始扫描,
用了@Component注解的类,自动ioc
base-package需要指定一个包的路径
-->
<context:component-scan base-package="cn.tedu.spring2"></context:component-scan>
</beans>
(3)读取配置文件,获取对象
package cn.tedu.test;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
//测试 注解开发
public class TestIOC2 {
@Test
public void ioc2(){
//1,读取核心配置文件
ClassPathXmlApplicationContext spring =
new ClassPathXmlApplicationContext(
"spring-config2.xml");
//2,getBean -- 参数是类名,但是首字母要变成小写才行
Object o = spring.getBean("user");
System.out.println(o);//cn.tedu.spring2.User@37d4349f
Object o2 = spring.getBean("userInfo");
System.out.println(o2);//cn.tedu.spring2.UserInfo@2805d709
}
}
三、模拟IOC的实现
1.创建Bean类,描述类的信息
//描述一个类的信息,spring把每个类当做一个bean
@Data
@AllArgsConstructor
public class Bean {
private String beanName;//类的名字
private String beanPath;//类的全路径
}
2.创建Spring容器,并指定要维护的类,提供getBean(),根据beanName获取对象
public class MyIOC {
//1,创建list,存好多bean
private List<Bean> beans = new ArrayList<>();
public MyIOC() throws Exception {
Bean b1 = new Bean("hello","cn.tedu.myioc.Hello");
Bean b2 = new Bean("user","cn.tedu.myioc.User");
beans.add(b1);
beans.add(b2);
//new
init();
}
Map<String,Object> map = new HashMap<>();
//2,创建map,存对象 { hello=new Hello() , user=new User()}
public void init() throws Exception {
//遍历list,获取每个bean
for(Bean b : beans){
String key = b.getBeanName();
String quanlujing = b.getBeanPath();
Object value = Class.forName(quanlujing).newInstance();
map.put(key,value);
}
}
//3,getBean()--根据key获取value
public Object getBean(String beanname){
return map.get(beanname) ;
}
}
3.创建类
4.测试
public class Test1 {
public static void main(String[] args) throws Exception {
MyIOC my = new MyIOC();
//根据类名,获取类的对象
Object o = my.getBean("hello");
System.out.println(o);//cn.tedu.myioc.Hello@54bedef2
//根据类名,获取类的对象
Object o2 = my.getBean("user");
System.out.println(o2);//cn.tedu.myioc.User@5caf905d
}
}
四、DI
1.概述
依赖注入,前提是先使用注解@Component完成IOC 两个类(对象)间的关系,使用@Autowried完成DI
2.测试SpringDI
(1)创建Teacher类
@Component//ioc
public class Teacher {
public String name = "jack" ;
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
'}';
}
}
(2)创建Student类
@Component//ioc
public class Student {
public String name = "蔡徐坤" ;
// 表示两个类之间的关系
@Autowired//DI依赖注入,自动装配,自动布线
Teacher t ;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", t=" + t +
'}';
}
}
(3) 创建核心配置文件,配置包扫描的路径
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--包扫描:扫描指定包路径下的所有类
哪些类有@Component注解,就帮哪些类new-ioc
-->
<context:component-scan base-package="cn.tedu.vo"></context:component-scan>
</beans>
(4)测试
//测试springdi
public class Test2 {
public static void main(String[] args) {
//1,读取核心配置文件
ClassPathXmlApplicationContext spring =
new ClassPathXmlApplicationContext(
"spring-config.xml");
//2,根据类名,获取对象
//右侧返回的是Object父类,左侧要子类--需要强转--向下转型--是为了使用子类的功能
Teacher t = (Teacher) spring.getBean("teacher");
System.out.println(t);//Teacher{name='jack'}
Student s = (Student) spring.getBean("student");
//DI:在获取学生信息的同时,也获取到了关联的老师信息
System.out.println(s);//Student{name='蔡徐坤', t=Teacher{name='jack'}}
}
}
3.DI的自动装配
利用注解方式,我们只需要写@Autowired注解,底层就会去容器中找对应的对象,如果有获取到,反射调用其对应的set方法,设置。而这个调用过程都是自动,我们没有手工去写set方法。所以这个过程也称为自动装配。
五、AOP面向切面编程
1.概念
Spring核心特征中除了IoC控制反转、DI依赖注入,还有一个核心就是强大的面向切面编程AOP(Aspect Oriented Programming)的实现。
2.Spring AOP有三要素
(1)Aspect定义切面;
(2)通过通知(Advice)来指定具体做什么事情。如方法执行前做什么,方法执行后做什么,抛 出异常做什么,从而实现对象行为(方法)的增强;
(3)具体通过切点(PointCut)配置切点表达式(expression)来指定在哪些类的哪些方法上织 入(ware)横切逻辑;被切的地方叫连接点(JoinPoint);
3.通知的执行顺序
Spring框架实现了AOP面向切面,其引入了第三方AspectJ框架来具体实现。AspectJ提供了五种切入方式,术语称为通知advice。
具体五种为:
(1)前置通知before
(2)后置通知after
(3)环绕通知around
(4)返回后通知afterReturning
(5)异常通知afterThrowing。
可以看到,分别在业务方法(Business Method)的执行前后进行拦截,执行指定的代码。
4.多切面执行顺序
下面是 两个切面 各通知的执行顺序:
5.测试
(1)需求:
(2)添加依赖
<dependencies>
<!--添加aop依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
(3)测试
@Component
@Aspect 切面:由切点和通知组成
public class AspectTest {
//切点表达式: *表示1个 ..表示多个 *依次代表方法返回值,类名,方法名,(..)是参数列表
@Pointcut("execution( * cn.tedu.service..*.*(..))")
public void pointcut(){}
//添加环绕通知,在业务方法执行前后添加功能
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object o = joinPoint.proceed();//执行业务方法并返回结果
long end = System.currentTimeMillis();
String methodname = joinPoint.getTarget().getClass().getName()//获取类名
+"."+joinPoint.getSignature().getName();//获取方法名
System.out.println("根据aop统计,"+methodname+"方法的耗时是:"+(end-start));
return o;//执行完方法的返回值
}
}