Spring

Spring 概述#

  1. Spring 是一个 轻量级的 J2EE 开源框架
  2. 可以解决企业应用开发的复杂性
  3. Spring 的核心部分:IOC 和 AOP
  4. 特点
    • 方便解耦,简化开发
    • AOP 编程
    • 方便程序测试
    • 方便与其他框架整合
    • 方便事务操作
    • 降低API开发难度

IOC(控制反转)#

把对象的创建和对象间的调用交给 Spring容器管理

目的: 降低 耦合度

底层原理:

  • xml解析
  • 工厂模式
  • 反射

IOC思想#

  1. IOC 思想基于 IOC容器完成, IOC 容器底层 就是 对象工厂

  2. Spring 提供IOC容器的两个接口(方法)

    • BeanFactory: IOC 容器基本实现,是Spring内部的使用接口,不提供给开发者使用

      加载配置文件的时候不会创建对象,只有使用的时候才会创建对象(懒汉式)

    • ApplicationContext: BeanFactory 的子接口,提供更强大的功能

      加载配置文件就会创建对象

ICO操作 Bean管理#

  1. 基于 xml 文件
  2. 基于注解

DI(依赖注入)#

DI 是 IOC中的一种具体实现,DI 需要在创建对象的基础之上完成

  • 构造器注入
  • set方法注入
  • 属性注入

Bean的生命周期#

五步

  1. 通过构造器创建Bean实例(无参构造)
  2. 属性注入(为Bean的属性设置值,或者对 其他Bean 引用)
  3. 调用 Bean 的初始化方法(需要进行配置)
  4. 使用
  5. 销毁(需要配置销毁的方法)

七步(多出的两部分别在 初始化前后执行)

这个是使用 bean 的后置处理器 BeanPostProcessor (要在配置文件中配置才能生效)

创建一个类 实现 BeanPostProcessor,重写方法

前置增强postProcessBeforeInitialization

后置增强 postProcessAfterInitialization

  1. 通过构造器创建Bean实例(无参构造)
  2. 属性注入(为Bean的属性设置值,或者对 其他Bean 引用)
  3. postProcessBeforeInitialization前置增强
  4. 调用 Bean 的初始化方法(需要进行配置)
  5. postProcessAfterInitialization后置增强
  6. 获取
  7. 销毁

把代码第四步❌剔除

MyBeanPostProcessor.java后置处理器

package com.bikakaso.processor;
import com.bikakaso.pojo.Orders;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import javax.annotation.PostConstruct;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("3. 我是前置增强");
if (bean instanceof Orders) {
Orders orders = (Orders) bean;
orders.setName("截胡了");
return orders;
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("6. 后置增强");
if (bean instanceof Orders) {
Orders orders = (Orders) bean;
orders.setName("不好意思,不让你截");
return orders;
}
return bean;
}
}

Orders.java实体类

package com.bikakaso.pojo;
import org.springframework.beans.factory.InitializingBean;
public class Orders implements InitializingBean {
private String name;
private double price ;
int i = 1;
public void setPrice(double price) {
this.price = price;
if ( i == 1) {
System.out.println("调用 setPrice 方法");
}
i++;
}
public Orders() {
System.out.println(" 1. 调用 无参构造");
}
public void setName(String name) {
this.name = name;
if ( i == 1) {
System.out.println("2.调用 setName 方法");
}
i++;
}
public void initMethod() {
System.out.println("5. 初始化方法,这个步骤需要自己配置");
}
public void destroyMethod() {
System.out.println("8. 销毁bean");
}
@Override
public String toString() {
return "Orders{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
// 这一步 实体类需要实现 InitializingBean 接口
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("4. 前置处理后,初始化前");
}
}

bean2.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="orders" class="com.bikakaso.pojo.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="订单1"/>
</bean>
<bean id="myBeanPostProcessor" class="com.bikakaso.processor.MyBeanPostProcessor"/>
</beans>

测试

@Test
public void test2() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean2.xml");
Orders orders = applicationContext.getBean("orders", Orders.class);
// Orders orders2 = applicationContext.getBean("orders", Orders.class);
System.out.println("7. 获取到 bean");
System.out.println(orders.toString());
applicationContext.close();
}

xml自动装配(自动注入)#

byName#

根据定义的属性名 注入,配置文件中的 bean 的 id 需要和 属性名相同

但是 byName 找不到 相同的 id 时,还会再根据 类型注入

Empl.java

public class Empl {
// 自动装配 byName, 配置文件中 通过 dept 这个名字 注入属性
private Dept dept;
private Dept dept1; // 测试 根据类型注入,3行需注释,重写 set 方法
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Empl{" +
"dept=" + dept +
'}';
}
}

Dept.java

public class Dept {
@Override
public String toString() {
return "Dept{}";
}
}

bean.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="empl" class="com.bikakaso.autowire.Empl" autowire="byName"/>
<bean id="dept" class="com.bikakaso.autowire.Dept"/>
<bean id="dept1" class="com.bikakaso.autowire.Dept"/> // 测试根据类型注入,10行需注释
</beans>

测试

// 自动装配
@Test
public void test3() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
Empl empl = applicationContext.getBean("empl", Empl.class);
System.out.println(empl.toString());
}

结果

正常情况

image-20230304200127694

找不到 name

image-20230304200023599

byType#

byType 是根据 bean 的 class 属性来注入的,同一个类的 bean 只能存在一个,否则会报错

image-20230304200550598

AOP#

动态代理#

1. jdk 动态代理

jdk 动态代理 必须 是 实现接口的类 (只能用于实现了接口的类产生代理)


创建 一个 实现 UserDao 的类, 在代理类中增强其方法

image-20230308202238745

使用 Proxy 类中的 newProxyInstance方法创建代理对象,方法中的三个参数

  1. 类加载器
  2. 增强方法所在的类,这个类实现的接口(支持多个接口)
  3. 实现InvocationHandler,创建代理对象,写增强的方法
2. Cglib动态代理

针对没有实现接口的类产生代理,应用的是底层的字节码增强技术,生成当前类的

子类对象。


创建当前类 的 子类的代理对象

image-20230308202812032

AOP 操作#

  1. Aspectj注解
  2. Aspectj配置 XML 文件

依赖

<!--AspectJ 开始-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
1. Aspectj注解

User.java

@Component
public class User {
public void add() {
// int a = 1 / 0; // @AfterThrowing 时使用
System.out.println("添加");
}
}

UserProxy.java

@Component
@Aspect // 生成代理对象
public class UserProxy {
// 前置通知
@Before(value = "execution(* com.bikakaso.aopanno.User.add(..))")
public void before() {
System.out.println("Before.........");
}
// 环绕通知
@Around(value = "execution(* com.bikakaso.aopanno.User.add(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("Around前.........");
point.proceed();
System.out.println("Around后.........");
}
// 最终通知
@After(value = "execution(* com.bikakaso.aopanno.User.add(..))")
public void after() {
System.out.println("After.........");
}
// 异常通知(无异常不会通知)
@AfterThrowing(value = "execution(* com.bikakaso.aopanno.User.add(..))")
public void afterThrowing() {
System.out.println("AfterThrowing.........");
}
// 后置/返回通知(无异常才会运行)
@AfterReturning(value = "execution(* com.bikakaso.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("AfterReturning.........");
}
}
// 抽取公共切入点
// 定义一个方法,使用 @Pointcut 注解
@Pointcut(value = "execution(* com.bikakaso.aopanno.User.add(..))")
public void pointDemo() {}
// 在通知的注解上属性直接使用方法名
@Before(value = "pointDemo()")
public void before() {
System.out.println("Before.........");
}

bean1.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
https://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.bikakaso.aopanno"/>
<!-- 开启代理-->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
</beans>

测试

@Test
public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
User user = context.getBean("user", User.class);
user.add();
}

结果:

增强方法中无异常

环绕前通知 ----> 前置通知 ----> 被增强的方法 ----> 后置通知 ----> 最终通知 ----> 环绕后通知

image-20230315205447016

增强方法中有异常

环绕前通知 ----> 前置通知 ---->异常通知 ----> 最终通知

image-20230315205636594

当多个类对一个方法增强时可以设置类的优先级,使用类注解 @Order(数字值),值越小,优先级越高

完全使用注解开发

  1. 创建配置类,添加 @Configuration
  2. 添加注解扫描 @ComponentScan("com.bikakaso")
  3. 添加 开启代理注解 @EnableAspectJAutoProxy(proxyTargetClass = true)(默认为 false)
@Configuration
@ComponentScan("com.bikakaso")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AnnoConfig {
}
@Test
public void test1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnoConfig.class);
User user = context.getBean("user", User.class);
user.add();
}

Spring 注解#

主要解释 @Bean 注解

从广义上分为两种:

  1. 注册Bean

    • @Component
    • @Repository
    • @Service
    • @Controller
    • @Configration(用于配置类)
    • @Bean
    • @Import

    前 4 个注解其实没什么太大区别,用法都一样,只是因为习惯将 @Service 用于Service层(业务逻辑), @Controller 用于Controller(控制层)层,@Repository 用于 Mapper 层, @Component 用于其他需要交给 Spring 容器管理的类


    注册 Bean 的注解,一方面是交给 Spring 容器管理方便解耦,另一方面是可以二次使用

  2. 使用Bean

    • @Autowired // 默认按type注入
    • @Qualifier //一般作为@Autowired()的修饰用
    • @Resource// //默认按name注入,可以通过name和type属性进行选择性注入

@Bean注解#

@Bean 注解用于告诉方法,产生一个 Bean 对象,将 这个Bean对象交给Spring容器管理,产生Bean对象的方法Spring只会调用一次,Spring会将这个Bean对象放在自己的IOC容器中

实际上,@Bean 注解和 xml 配置文件中的 bean 标签的作用是一样的

为什么要使用 @Bean 注解

类似于 @Component @Repository @Controller @Service 这些注解存在局限性,只能作用于自己编写的类. @Bean 注解写在配置文件中,可以全局使用,而且可以动态获取一个Bean对象,能够根据不同的环境得到不同的Bean对象
@Import也能把第三方库中的类实例交给spring管理,而且@Import更加方便快捷

@Bean总结
  1. @Bean 注解告诉方法,产生一个Bean对象,然后将 这个Bean对象交给Spring管理,产生这个Bean对象的方法Spring 只会调用一次,随后Spring会将Bean对象放在自己的IOC容器中
  2. @Component , @Repository , @ Controller , @Service 这些注解只局限于自己编写的类,而@Bean注解能把第三方库中的类实例加入IOC容器中并交给spring管理。
  3. @Bean注解的另一个好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。
  4. 记住,@Bean就放在方法上,就是让方法去产生一个Bean,然后交给Spring容器,剩下的你就别管了

作者:Bikakaso

出处:https://www.cnblogs.com/Bikakaso/p/spring.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Bikakaso  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示