Spring
Spring 概述#
- Spring 是一个 轻量级的 J2EE 开源框架
- 可以解决企业应用开发的复杂性
- Spring 的核心部分:IOC 和 AOP
- 特点
- 方便解耦,简化开发
- AOP 编程
- 方便程序测试
- 方便与其他框架整合
- 方便事务操作
- 降低API开发难度
IOC(控制反转)#
把对象的创建和对象间的调用交给 Spring容器管理
目的: 降低 耦合度
底层原理:
- xml解析
- 工厂模式
- 反射
IOC思想#
IOC 思想基于 IOC容器完成, IOC 容器底层 就是 对象工厂
Spring 提供IOC容器的两个接口(方法)
BeanFactory
: IOC 容器基本实现,是Spring内部的使用接口,不提供给开发者使用加载配置文件的时候不会创建对象,只有使用的时候才会创建对象(懒汉式)
ApplicationContext
: BeanFactory 的子接口,提供更强大的功能加载配置文件就会创建对象
ICO操作 Bean管理#
- 基于 xml 文件
- 基于注解
DI(依赖注入)#
DI 是 IOC中的一种具体实现,DI 需要在创建对象的基础之上完成
- 构造器注入
- set方法注入
- 属性注入
Bean的生命周期#
五步
- 通过构造器创建Bean实例(无参构造)
- 属性注入(为Bean的属性设置值,或者对 其他Bean 引用)
- 调用 Bean 的初始化方法(需要进行配置)
- 使用
- 销毁(需要配置销毁的方法)
七步(多出的两部分别在 初始化前后执行)
这个是使用 bean 的后置处理器 BeanPostProcessor
(要在配置文件中配置才能生效)
创建一个类 实现 BeanPostProcessor
,重写方法
前置增强postProcessBeforeInitialization
后置增强 postProcessAfterInitialization
- 通过构造器创建Bean实例(无参构造)
- 属性注入(为Bean的属性设置值,或者对 其他Bean 引用)
postProcessBeforeInitialization
前置增强- 调用 Bean 的初始化方法(需要进行配置)
postProcessAfterInitialization
后置增强- 获取
- 销毁
把代码第四步❌剔除
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()); }
结果
正常情况
找不到 name
byType#
byType 是根据 bean 的 class 属性来注入的,同一个类的 bean 只能存在一个,否则会报错
AOP#
动态代理#
1. jdk 动态代理
jdk 动态代理 必须 是 实现接口的类 (只能用于实现了接口的类产生代理)
创建 一个 实现 UserDao 的类, 在代理类中增强其方法
使用
Proxy
类中的newProxyInstance
方法创建代理对象,方法中的三个参数
- 类加载器
- 增强方法所在的类,这个类实现的接口(支持多个接口)
- 实现InvocationHandler,创建代理对象,写增强的方法
2. Cglib动态代理
针对没有实现接口的类产生代理,应用的是底层的字节码增强技术,生成当前类的
子类对象。
创建当前类 的 子类的代理对象
AOP 操作#
- Aspectj注解
- 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(); }
结果:
增强方法中无异常
环绕前通知 ----> 前置通知 ----> 被增强的方法 ----> 后置通知 ----> 最终通知 ----> 环绕后通知
增强方法中有异常
环绕前通知 ----> 前置通知 ---->异常通知 ----> 最终通知
当多个类对一个方法增强时可以设置类的优先级,使用类注解 @Order(数字值)
,值越小,优先级越高
完全使用注解开发
- 创建配置类,添加 @Configuration
- 添加注解扫描 @ComponentScan("com.bikakaso")
- 添加 开启代理注解 @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 注解
从广义上分为两种:
注册Bean
- @Component
- @Repository
- @Service
- @Controller
- @Configration(用于配置类)
- @Bean
- @Import
前 4 个注解其实没什么太大区别,用法都一样,只是因为习惯将 @Service 用于Service层(业务逻辑), @Controller 用于Controller(控制层)层,@Repository 用于 Mapper 层, @Component 用于其他需要交给 Spring 容器管理的类
注册 Bean 的注解,一方面是交给 Spring 容器管理方便解耦,另一方面是可以二次使用
使用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总结
- @Bean 注解告诉方法,产生一个Bean对象,然后将 这个Bean对象交给Spring管理,产生这个Bean对象的方法Spring 只会调用一次,随后Spring会将Bean对象放在自己的IOC容器中
- @Component , @Repository , @ Controller , @Service 这些注解只局限于自己编写的类,而@Bean注解能把第三方库中的类实例加入IOC容器中并交给spring管理。
- @Bean注解的另一个好处就是能够动态获取一个Bean对象,能够根据环境不同得到不同的Bean对象。
- 记住,@Bean就放在方法上,就是让方法去产生一个Bean,然后交给Spring容器,剩下的你就别管了
作者:Bikakaso
出处:https://www.cnblogs.com/Bikakaso/p/spring.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
本文来自博客园,作者:Bikakaso,转载请注明原文链接:https://www.cnblogs.com/Bikakaso/p/spring.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)