PancrasL的博客

Spirng5快速入门

2021-03-26

1. 基本概念

(1)轻量级的JavaEE开发框架

(2)Spring的两个特性

  • IOC:控制反转,将创建对象的过程交给Spring进行管理
  • AOP:面向切面,不修改源代码的情况加进行功能增强

2. IOC容器

控制反转:从主动创建对象变为被动接受对象(例如使用setter方式创建)

IOC是一个容器,帮我们管理所有的组件。

容器启动的时候创建所有单实例对象。

我们可以从直接从容器中获取到这个对象。

DI是实现IOC的一种方法。

  • Question 1:IOC容器的启动过程?启动期间都做了什么?

      1.  // 构造器ApplicationContext ioc = new ClassPathXmlApplicationContext("ioc.xml"); // 构造器
        
        1
        2
        3
        4
        5
        6
        7

        2. 执行refresh()

        3.

        4. ```java
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 解析xml文件
      2. finishBeanFactoryInitialization(beanFactory); // 初始化bean
        //调用了↓进行初始化
        beanFactory.preInstantiateSingletons();
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15

        6. ```java
        // 获取需要创建的单实例对象
        List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

        // 获取代创建bean的类信息
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

        // 创建非抽象单实例对象
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()){
        // 创建对象
        getBean(xx);
        }

        // 创建好的对象最终保存在map中
  • Question 2:IOC是如何创建并管理单实例bean的?保存到了哪里?

2.1 IOC概念和原理

2.1.1 基本概念

  1. 什么是IOC

    (1) 控制反转:把对象创建和对象之间的调用过程交给Spring进行管理

    (2) 使用IOC的目的:降低耦合度

    • 控制:资源的获取方式
      • 主动式:使用new方式创建资源,复杂对象的创建过于繁琐、麻烦。
      • 被动式:资源的创建过程由容器来创建和设置,我们直接使用资源即可。
    • 容器:管理所有的组件(有功能的类)。
    • 反转:将资源创建的过程交给Spring来做。
    • DI,Denpendency Injection:依赖注入,通过反射创建组件。

2.1.2 底层原理

  1. IOC底层原理

    (1)xml解析、工厂模式、反射

2.2 IOC操作Bean管理(基于注解)

tips:使用xml管理bean,使用注解注入属性

2.2.1 使用注解创建对象

  • @Component:控制器,推荐给控制器层(servlet包下的组件)添加

  • @Service:业务逻辑,推荐个service包下的组件添加

  • @Repository:数据库层(持久化层、dao层)的组件添加

  • @Controller:不属于以上三层的组件

1
2
3
4
5
6
@Component(value="userService")
public class UserService {
public void add(){
System.out.("service add....");
}
}
1
<context:component-scan base-package="indi.pancras.spring5"></context:component-scan>
1
2
3
4
5
6
@Test
void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
}

2.2.2 使用注解注入属性

A. @Autowired

根据属性类型进行注入

原理:

  • 按照类型区容器中寻找对应的组件:bookService = ioc.getBean(BookService.class);
    • ① 找到一个就赋值
    • ② 没找到,抛异常(或者可通过设置required=false设置为null
    • ③ 找到多个,按照变量名作为id进行匹配:bookService = ioc.getBean(“bookService”);
1
2
3
4
5
6
7
8
9
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add....");
userDao.update();
}
}

方法上有@Autowire时,方法的每一个参数都会自动注入值

B. @Qualifier

当同一个接口有两个实现类时,使用 @Qualifier 指定id进行装配

1
2
3
4
5
6
7
8
9
10
11
// UserService.java
@Service
public class UserService
@Autowired
@Qualifier(value = "userDaoImpl2")
private UserDao userDao;
public void add() {
System.out.println("service add....");
userDao.update();
}
}

C. @Resource

根据属性类型或属性名称注入

属于java拓展包里的内容,不建议使用

D. @Value

注入普通类型属性,和配置文件搭配使用

1
2
3
4
5
6
7
8
@Service
public class UserService {
@Value(value = "my name")
private String name;
public void add() {
System.out.println("service add...."+ name);
}
}

2.3 Bean的作用域和生命周期

  • 默认单实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
	@Test
void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user", User.class);

System.out.println(user1);
System.out.println(user2);
}
/*
indi.pancras.spring5.User@f107c50
indi.pancras.spring5.User@f107c50
*/
  • 通过scope属性配置作用域
1
2
3
4
5
<!-- 单实例 -->
<bean id="user" class="indi.pancras.spring5.User" scope="singleton"></bean>

<!-- 多实例 -->
<bean id="user" class="indi.pancras.spring5.User" scope="prototype"></bean>

2.4 Java配置类

使用@Configuration表明,等效于xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class MyConfiguration {
// 方法名就是bean的id
@Bean
public User getUser(){
return new User();
}
}

@Test
void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
}

3 AOP

3.1 AOP概念和原理

3.1.1 什么是AOP

  • 定义:AOP(Aspect Oriented Programming):面向切面编程,指在程序运行期间,将某段代码动态的切入到**指定方法的指定位置**进行运行的编程方式。
  • 场景:计算器运行计算方法的时候进行日志记录
    • 直接编写在方法内部:耦合度太高,修改维护麻烦,不推荐
    • 使用动态代理加入日志
  • JDK动态代理缺陷:如果类没有实现任何接口,则不能创建代理。
  • Spring动态代理:实现简单,无需要求被代理类实现接口
  • AOP的底层:动态代理

降低模块的耦合度、添加新的功能而不修改源代码

3.1.2 AOP底层原理

使用动态代理:

  • 有接口:使用JDK动态代理
  • 无接口:使用CGLIB动态代理

3.1.3 专业术语

  • 连接点:每一个方法的每一个可以被通知的位置
  • 切入点:连接点的子类,实际被插入代码的
  • 通知(增强):实际增强的逻辑
    • 前置通知:方法执行前执行,@Before
    • 后置通知:方法执行后执行,@After
    • 环绕通知:方法执行前后执行,@Around
    • 异常通知:方法出现异常时执行,@AfterThrowing
    • 返回通知:方法返回后执行,@AfterReturning
  • 切面:把通知应用到切入点的过程

(4) 使用场景

  • AOP+日志保存到数据库中
  • 权限认证
  • 安全检查
  • 事务控制

3.2 AOP操作

Spring框架一般基于AspectJ实现AOP操作

3.2.1 切入点表达式

  • execution()
1
2
3
4
5
6
7
8
// 对indi.pancras.spring5.User类里面的add()进行增强
execution(* indi.pancras.spring5.User.add(..))

// 对indi.pancras.spring5.User类里面的所有方法进行增强
execution(* indi.pancras.spring5.User.*(..))

// 对indi.pancras.spring5包里的所有类的所有方法进行增强
execution(* indi.pancras.spring5.*.*(..))

3.2.2 一个示例

  • bean5.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 开启注解扫面 -->
<context:component-scan base-package="indi.pancras.spring5.aopanno"></context:component-scan>
<!-- 开启Aspect生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  • User.java
1
2
3
4
5
6
@Component
public class User {
public void add(){
System.out.println("add ...");
}
}
  • UserProxy.java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
@Aspect
public class UserProxy {
@Before("execution(* indi.pancras.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before...");
}

@After("execution(* indi.pancras.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after...");
}
}
  • test.java
1
2
3
4
5
6
7
8
class UserProxyTest {
@Test
void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
User user = context.getBean("user", User.class);
user.add();
}
}

Reference: