Spring IOC和AOP

系列:Spring 01:IOC和AOP

1. 概述

1.1 核心特性

依赖注入 dependency injection

面向切面编程 aspect-oriented programming

重量级的企业级Java技术,尤其是EJB

相对于EJB来说,Spring提供更加轻量级和简单的编程模型。它增强了简单老式Java对象(POJO)的功能,使其具备了之前只有EJB和其他企业级Java规范才具有的功能

1.2 由来

Spring是为了解决企业级应用开发的复杂性而创建的。为了降低Java开发的复杂性,Spring采取了以下四种关键策略

  • 基于POJO的轻量级和最小侵入性编程
  • 通过依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样板式代码

1.3 优点

  1. 低侵入式设计,代码污染极低
  2. Spring的DI机制降低了业务对象替换的复杂性,提高了组件之间的解耦
  3. Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而虎丘更好的复用
  4. Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问
  5. Spring并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

1.4 Spring家族组成

在这里插入图片描述

小结

IOC和DI,DI是实现IOC的一种手段

IoC,Inverse Of Control,控制反转:对象之间的依赖关系由容器来创建和管理

DI,Dependency Injection,依赖注入:在使用Spring容器时,容器通过调用setter或构造器方法来建立对象之间的 依赖关系

DI能够让相互协作的软件保持松耦合,而AOP允许将遍布应用各处的功能抽离出来形成可重用的组建。


2. Spring容器

作用:

  • 创建、配置、装配bean
  • 管理bean的生命周期

实现:Application的多种应用上下文实现,装载bean的定义并把他们组装起来,区别在于加载配置的方式不同

BeanFactory
ApplicationContext
    - AnnotationConfigApplicationContext
    - AnnotationConfigWebApplicationContext
    - ClassPathXmlApplicationContext
    - FileSystemXmlApplicationContext
    - XmlWebApplicationContext

ApplicationContext比起BeanFactory,增加了:

对Spring AOP特性更好的集成

消息资源处理(用于国际化)

事件发布

应用层特定上下文,如用于Web应用程序的WebApplicationContext

3. Bean

3.1 配置bean的方式

  • xml
  • 注解:@Component@Service@Repository
  • Java:@Configuration@Bean
// 注解
@Repository
public class UserDao {
    public List<User> select() {
        List<User> users = new ArrayList<>();
        // ..
        return users;
    }
}

// Java Config
@Configuration
public class UserConfig {
    @Bean
    public User user() {
        return new User();
    }
}

3.2 如何发现bean

  1. 组件扫描:@ComponentScan。默认扫描配置类相同的包,可使用value或basePackages指定扫描包路径(除了包路径,还支持.class)
  2. 自动装配:@Autowired。
@Service
public class UserService {
    // 通过field反射注入
    @Autowired
    private UserDao userDao;
    
    // 通过构造器注入
    private UserDao userDao;
    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
    
    // 通过setter注入
    private UserDao userDao;
    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

PS:@Autowired推荐构造器或setter注入,具体选择视场景。参考

3.3 如何创建对象

  1. 构造器

  2. 静态工厂方法

  3. 实例工厂

3.4 注入属性的方式

  • 构造器
  • setter

3.5 作用域

ScopeDescription
singleton(Default) Scopes a single bean definition to a single object instance per Spring IoC container.
prototypeScopes a single bean definition to any number of object instances.
requestScopes a single bean definition to the lifecycle of a single HTTP request; that is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
sessionScopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
globalSessionScopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a Portlet context. Only valid in the context of a web-aware Spring ApplicationContext.
applicationScopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocketScopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

3.6 生命周期

BeanFactory的注释如下:生命周期方法调用顺序

BeanNameAware: setBeanName
BeanClassLoaderAware: setBeanClassLoader
BeanFactoryAware: setBeanFactory
EnvironmentAware: setEnvironment
EmbeddedValueResolverAware: setEmbeddedValueResolver
---- only applicable when running in an application context
ResourceLoaderAware: setResourceLoader
ApplicationEventPublisherAware: setApplicationEventPublisher
MessageSourceAware: setMessageSource
ApplicationContextAware: setApplicationContext
---- only applicable when running in a web application context
ServletContextAware: setServletContext
----
postProcessBeforeInitialization BeanPostProcessors
InitializingBean: afterPropertiesSet
[a custom init-method definition]
postProcessAfterInitialization  BeanPostProcessors
  1. 实例化;
  2. 设置属性值;
  3. 如果实现了BeanNameAware接口,调用setBeanName设置Bean的ID或者Name;
  4. 如果实现BeanFactoryAware接口,调用setBeanFactory 设置BeanFactory;
  5. 如果实现ApplicationContextAware,调用setApplicationContext设置ApplicationContext
  6. 调用BeanPostProcessor的预先初始化方法;
  7. 调用InitializingBean的afterPropertiesSet()方法;
  8. 调用定制init-method方法;
  9. 调用BeanPostProcessor的后初始化方法;
  10. 【使用】
  11. 调用DisposableBean的destroy();
  12. 调用定制的destroy-method方法;

3.7 其他

配置集合
public interface CalcHandler {
    double calc(double a, double b);
}
@Component
public class AddCalcHandler implements CalcHandler {
    @Override
    public double calc(double a, double b) {
        return a + b;
    }
}

@Component
public class SubCalcHandler implements CalcHandler {
    @Override
    public double calc(double a, double b) {
        return a - b;
    }
}
@Service
public class CalcService {
    private Map<String, CalcHandler> calcHandlerMap;

    @Autowired
    public CalcService(List<CalcHandler> calcHandlers) {
        calcHandlerMap = new HashMap<>();
        calcHandlers.forEach(calcHandler -> {
            calcHandlerMap.put(getBeanName(calcHandler.getClass().getSimpleName()), calcHandler);
        });
    }

    private String getBeanName(String clzName) {
        String ch = clzName.substring(0, 1).toLowerCase(Locale.ENGLISH);
        return ch + clzName.substring(1);
    }

    public double calc(String strategy, double var1, double var2) throws Exception {
        CalcHandler calcHandler =  calcHandlerMap.get(strategy);
        if (calcHandler == null) {
            throw new Exception("简化处理");
        }
        return calcHandler.calc(var1, var2);
    }
}
@Value

application.properties:

author.name = hzk

Test.java:

@Value("${author.name}")
private String authorName;
Java和XML配置互相导入

Java配置类引入Java配置类:@Import({A.class})

Java配置类引入XML配置文件:@ImportResource({"b.xml"})

XML配置文件引入XML配置文件:<import resource="b.xml" />

XML配置文件引入Java配置类:<bean class="com.hzk.app.AConfig" />

配置profile bean

指定bean或配置类的profile:@Profile(“dev”)

激活profile:spring.profiles.active,或spring.profiles.default

设置上述属性:

  • DispatcherServlet的初始化参数
  • Web应用的上下文参数
  • JNDI条目
  • 环境变量
  • JVM系统属性
  • 集成测试类(@ActiveProfiles)
条件化的bean

@Conditional系列

自动装配的歧义

@Primary,或@Qualifier

注入外部配置文件的值

声明属性源并 通过Environment检索属性

SpEL

语法:${…}

使用:字面值、引用bean 属性 方法、Java类型

运算符:算术、比较、逻辑、条件、正则等

3.8 事件处理

参考:https://juejin.im/post/6844903854027309070

内置标准事件
事件说明
ContextRefreshedEvent当 Spring 容器处于初始化或者刷新阶段时就会触发,事实是ApplicationContext#refresh()方法被调用时,此时容器已经初始化完毕。
ContextStartedEvent当调用 ConfigurableApplicationContext接口下的 start() 方法时触发,表示 Spring 容器启动;通常用于 Spring 容器显式关闭后的启动。
ContextStoppedEvent当调用 ConfigurableApplicationContext 接口下的 stop()方法时触发,表示 Spring 容器停止,此时能通过其 start()方法重启容器。
ContextClosedEvent当 Spring 容器调用 ApplicationContext#close() 方法时触发,此时 Spring 的 beans 都已经被销毁,并且不会重新启动和刷新。
RequestHandledEvent只在 Web 应用下存在,当接受到 HTTP 请求并处理后就会触发,实际传递的默认实现类 ServletRequestHandledEvent
demo

接口:

@Component
public class InitalizeListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        System.out.println("Spring容器启动,ApplicationContext对象:" + applicationContext);
    }
}

注解:

@Component
public class MyEvent {
    @EventListener(value = {ContextRefreshedEvent.class},
    condition = "1 > 0")
    public void listener(ApplicationEvent event) {
        System.out.println(event.toString());
    }
}
事件传递

@EventListener

事件侦听器优先级

@Order

自定义事件
  • Event:所需要触发的具体事件对象,通常扩展ApplicationEvent实现

  • Publisher:触发事件发布的对象,Spring提供了ApplicationEventPub

    lisher#publishEvent()发布事件

  • Listener:侦听事件的对象,即进行事件回调处理,通过实现ApplicationListener或@EventListener

异步事件处理:ApplicationEventMulticaster

Spring Boot事件
ApplicationStartingEvent:程序启动时发生。
ApplicationEnvironmentPreparedEvent:程序中Environment对象就绪时发生。
ApplicationPreparedEvent:程序启动后但还未刷新时发生。
ApplicationStartedEvent:程序启动刷新后发生。
ApplicationReadyEvent:程序启动完毕,等待请求时发生。
ApplicationFailedEvent:程序启动过程中出现异常时发生。

3.9 小结

延迟加载

默认情况下,容器启动之后会将所有作用域为单例的bean创建好;但是有的业务场景我们并不需要它提前都创建好;此时,我们可以在bean中设置lzay-init=“true”,这样,当容器启动之后,作用域为单例的bean,就不再创建。

bean的线程安全性

Spring框架并没有对单例的bean进行多线程的封装处理,线程安全问题和并发问题,需要我们开发者自己考虑。

一般情况下,Spring Bean没有可变的状态(如Service和Dao bean),可认为单例bean是线程安全的,如果bean有多种状态(如View Model bean),需要自行考虑线程安全问题。

4. 面向切面编程

4.1 定义

AOP,Aspect-oriented Programming,面向切面编程:是一种思想。它就是针对业务处理过程中的切面进行提取,以达到优化代码的目的,减少重复代码的目的。

比如,在编写业务逻辑代码的时候,我们习惯性的都要写:日志记录,事物控制,以及权限控制等,每一个子模块都要写这些代码,代码明显存在重复。这时候,我们运用面向切面的编程思想,采用横切技术,将代码中重复的部分,不影响主业务逻辑的部分抽取出来,放在某个地方进行集中式的管理,调用。 形成日志切面,事物控制切面,权限控制切面。

这样,我们就只需要关系业务的逻辑处理,即提高了工作的效率,又使得代码变的简洁优雅。这就是面向切面的编程思想,它是面向对象编程思想的一种扩展。

4.2 应用场景

缓存,权限管理,错误处理,日志,事务控制等

4.3 术语

1. 通知 Advice

在这里插入图片描述

2. 连接点 Join Point

在应用执行过程中能够插入切面的一个点。这个点是调用方法时、抛出异常时、甚至修改一个字段时。

3. 切点 Pointcut
4. 切面 Aspect
5. 引入 Introduction
6. 织入 WeAVing

切面在指定的连接点被织入到目标对象中。

在这里插入图片描述

4.4 实现原理

代理机制。

JDK的动态代理,基于接口,只有实现了接口的类才能产生代理类

Cglib代理,应用的是底层的字节码增强技术,生成当前类的子类对象

4.5 Spring AOP

Spring提供的4种类型的AOP支持:

  • 基于代理的经典Spring AOP
  • 纯POJO切面
  • @AspectJ注解驱动的切面
  • 注入式AspectJ切面

前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础上,因此,Spring对AOP的支持局限于方法拦截。

声明式Spring AOP样例demo

引入jar:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

接口:

@Component
public class Performance {
    public void perform() {
        System.out.println("perform");
    }
}

AOP类

@Aspect
@Component
public class PerformanceAOP {
    // 定义切点
    @Pointcut("execution(* com.hzk.app.ssm._06_aop.Performance.perform(..))")
    public void perform() {}

    @Before("perform()")
    public void beforePerform() {
        System.out.println("before perform()");
    }

    @AfterReturning("perform()")
    public void afterReturnningPerform() {
        System.out.println("after returnning perform()");
    }

    @AfterThrowing("perform()")
    public void afterThrowingPerform() {
        System.out.println("after throwing perform()");
    }

    @After("perform()")
    public void afterPerform() {
        System.out.println("after perform()");
    }
}

输出:

before perform()
perform
after returnning perform()
// 如果异常,上面一行则被替换成:after throwing perform()
after perform()

4.6 AspectJ

Spring AOP比起AspectJ,功能较弱,如以下功能:

  • 创建对象时应用通知

4.7 小结

环绕通知

切面关注方法,存在参数

通过切面为现有的方法增加额外的功能:@DeclareParents

参考

《Spring实战》

Spring 注入方式选择 https://www.cnblogs.com/joemsu/p/7688307.html

Spring 事件处理 https://juejin.im/post/6844903854027309070

Spring AOP https://www.cnblogs.com/joy99/p/10941543.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值