Spring深入学习

1 Bean创建的生命周期

Spring bean是Spring运行时管理的对象。Spring Bean的生命周期指的是Bean从创建到初始化再到销毁的过程,这个过程由IOC容器管理。

IOC即控制反转,是面向对象编程中的一种设计原则,通过依赖注入(DI)、依赖查找的方式实现对象之间的松耦合关系。

BeanFactory为IoC容器,ApplicationContext为应用上下文,ApplicationContext容器包括BeanFactory容器的所有功能。

1)创建bean的三种方式

  1. 基于XML配置文件
  2. 基于注解,@Component、 @Repository、@Service、@Controller;@Component可以代替@Repository、@Service、@Controller。
  3. 基于Java类的bean定义,需要提供setter方法
    @Beanpublic Student student(){return new Student();;}public class Student{private String name;public void SetName(String name){this.name = name;}}

 2)bean对象

 

对象不一定是bean,bean一定是对象,bean对象都放在一个MAP里。 

获取对象的方式可以使用构造方法去创建对象,上面的UserService就存在一个默认的构造方法。(如果程序中没有显式定义任何构造方法,那么java语言将自动提供一个隐含的默认构造方法。

spring扫描到@Component等注解时,就会认为这是定义的bean,就会使用此类构造方法获取对象。然后Spring去检查哪些对象存在@Autowired,就给自动进行依赖注入,进行赋值。

UserServie.class--->构造方法--->普通对象--->依赖注入--->放入Map<beanName,Bean对象>

spring会继续去检查哪些方法存在@PostConstruct方法,然后主动执行方法里的内容。 

实现逻辑如下:

当然也可以实现InitializingBean接口,用写afterPropertiesSet()方式实现

((InitializingBean)对象).afterPropertiesSet(); 

UserServie.class--->推断构造方法--->普通对象--->依赖注入--->初始化前(@PostConstruct)--->初始化(InitializingBean)--->初始化后(AOP)--->代理对象--->放入Map<beanName,Bean对象>

推断构造方法:

@Service
public class UserService {private final UserRepository userRepository;@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}
}

写了无参构造,则默认使用无参构造依赖注入;

不写无参构造,写多个有参构造,会报错No Default Construct;必须使用@Autowired指定默认是是哪个;

不写无参构造,只写一个有参构造,则会直接使用这个有参构造进行依赖注入;

依赖注入完成属性赋值,spring会依据入参的类型、名字去spring IOC容器里的Bean MAP<beanname,bean对象>里寻找bean对象。

1.1 依赖注入

在Java中,DI的实现方式主要有以下几种:

● 构造器注入
● Setter方法注入
● 接口注入
● 注解注入

1)构造器注入(spring框架中在构造方法上添加@Autowired注解)

@Component 标准一个普通的spring Bean类; @Repository标注一个DAO组件类; @Service标注一个业务逻辑组件类。 @Component可以代替@Repository、@Service、@Controller,因为这三个注解是被@Component标注的。

@Service
public class UserService {private final UserRepository userRepository;@Autowiredpublic UserService(UserRepository userRepository) {this.userRepository = userRepository;}public User getUserById(int id) {return userRepository.getUserById(id);}
}

2)setter方法注入(在setter方法上添加@Autowired注解)

@Service
public class UserService {private UserRepository userRepository;@Autowiredpublic void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}public User getUserById(int id) {return userRepository.getUserById(id);}
}

3)接口注入

@Service
public class UserService {private UserRepository userRepository;@Autowiredpublic void setUserRepositorySetter(UserRepositorySetter userRepositorySetter) {userRepositorySetter.setUserRepository(userRepository);}public User getUserById(int id) {return userRepository.getUserById(id);}
}

实现对应的接口

public interface UserRepositorySetter {void setUserRepository(UserRepository userRepository);
}@Repository
public class UserRepositoryImpl implements UserRepository, UserRepositorySetter {@Overridepublic User getUserById(int id) {// 实现代码}@Overridepublic void setUserRepository(UserRepository userRepository) {// 实现代码}
}

4)注解注入

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User getUserById(int id) {return userRepository.getUserById(id);}
}

依赖注入如何寻找是哪个 userRepository?过程如下,从by type 到 by name:

1.2 AOP

认识AOP

AOP 是 Aspect Oriented Programming 的缩写,译为面向切向编程。

设计一个日志打印模块: 

  • 按 OOP 思想,我们会设计一个打印日志 LogUtils 类,然后在需要打印的地方引用即可。
  • 按AOP思想,声明哪些地方需要打印日志,这个地方就是一个切面,AOP 会在适当的时机为你把打印语句插进切面。

AOP实现的关键在于AOP框架自动创建的AOP代理,以AspectJ为代表的静态代理,以Spring AOP为代表的动态代理。Spring AOP中的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理。

Spring AOP通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean(目标对象)。

AOP实现技术由APT、AspetJ等,如下:

1)横切关注点

跨越应用程序多个模块的方法或功能,如日志、安全、缓存、事务等等。

2)连接点

连接点是在应用执行中能够插入切面的一个点。即程序执行过程中能够应用通知的所有点。

3)通知

切面的工作被称为通知,Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能。
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么。
  • 返回通知(After-returning):在目标方法成功执行之后调用通知。
  • 异常通知(After-throwing)):在目标方法抛出异常后调用通知。
  • 环绕通知(Around) :通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

1.3 SpringBoot AOP

orderService为空? 

进入test之前,orderServcie是有值的!

1)Spring首先要判断这个对象要不要执行AOP

2)生成一个子类代理类,继承UserService,重写UserService里的test方法

3)子类代理类(代理对象)执行test方法时,先执行切面逻辑,然后执行业务逻辑

4)执行 代理对象.target.test()方法

        代理对象.target对象 = 被代理对象 = 普通对象 = 已经经过了依赖注入 = 对象已经属性有值;

        但是代理对象没有依赖注入,没有值;他只是为了执行切面逻辑,所以不需要必须去用对象的属性。

切面逻辑中可以通过获取这个target对象,来获取普通对象

AOP基本使用: 

依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>代码
@Aspect  // 使用@Aspect注解声明一个切面
@Component
public class SysLogAspect {@Autowiredprivate SysLogService sysLogService;@Pointcut("@annotation(com.lmlsj.SysLog)")public void logPointCut() {}@Before("execution(* com.lmlsj.SysLog.*(..))")public void before() {System.out.println("MyAspect: 前置增强.....");}
}配置
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}

正常流程:【环绕通知-前】-> 【前置通知】-> 【返回通知】-> 【后置通知】->【环绕通知-后】。 

2 事物

1)事物执行逻辑

2)普通对象调用,事物失效

这里a()方法是普通对象的,直接执行a()里面的语句,不会去走切面逻辑判断是否有@Transactional注解,事物失效。 

3)代理对象调用才会去走切面逻辑

自己注入自己,使用代理对象实现事物正常执行

4)@Configuration

加上@Configuration后,Appconfig就是代理对象,才能保证下图两处的dataSource()是同一个对象,才能执行事务。

3 扫描

Spring扫描首先Spring根据注解去寻找bean类,非懒加载的bean在Spring容器启动时就创建好,懒加载的bean是用到时再创建。如何寻找注解的实现思路:

1)反射方式:AA.class.isAnnotationPresent(Component.class)

2)ASM技术(Spring使用):编辑CLASS字节码

扫描类的寻找:

AnnotationConfigApplicationContext.java

1)处理配置类

(1)判断Component注解

ConfigurationClassParser.java  doProcessConfigurationClass(*)

(2)检查是不是配置类的地方

2)ComponentScan

parse解析得到BeanDefinition集合,然后去遍历BeanDefinition这些对象是不是有什么注解、配置

 3)parse属性解析过程

ComponentScanAnnotationParser类

this.registry是Spring IOC容器

(1) useDefaultFilters属性,注册一些默认的过滤器

(2)ComponentScan的nameGenerator属性

generatorClass是nameGenerator属性的管理,没有设置该属性就使用默认值。

补充:可以根据@Component的value设置bean的名字,没有设置就根据类名进行设置。

前两个字符都是大写,就直接返回;不符合就将第一个字符,设置为小写。

(3)scopeProxy属性设置

作用域在类上设置,在收到请求时再创建bean

同时可以指定代理对象的生成方式

(4)resourcePattern、includeFilters、excludeFilters属性

excludeFilters可以设置某个类不是一个bean,type = FilterType.ASSIGNABLE_TYPE是根据类class来过滤

@ComponentScan(value = "com.lmlsj", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {MyService.class})})

(5)懒加载

(6)扫描路径属性

根据属性值配置扫描路径,或者根据@ComponentScan注解配置扫描路径

排除器,排除已经设置成bean的类 

最后就是doScan真正的扫描开始。

4)doScan扫描

扫描包路径

 public Set<BeanDefinition> findCandidateComponents(String basePackage) {return this.componentsIndex != null && this.indexSupportsIncludeFilters() ? this.addCandidateComponentsFromIndex(this.componentsIndex, basePackage) : this.scanCandidateComponents(basePackage);}

检查是否在过滤器里

4 Spring启动demo

1)SpringApplicationContex

public class SpringApplicationContext {private Class configClass;private Map<String,BeanDefination> beanDefinationMap = new HashMap<>();private Map<String,Object> singletonObjects = new HashMap<>();  //单例池private List<BeanPostProcess> beanPostProcessorList = new ArrayList<>();public SpringApplicationContext(Class configClass) {this.configClass = configClass;scan(configClass);              //解析传进来的配置类,生成beanDefinationpreInstantiateSingletons();     //单例bean初始化}private void preInstantiateSingletons() {for(Map.Entry<String,BeanDefination> entry : beanDefinationMap.entrySet()){BeanDefination beanDefination = entry.getValue();if(beanDefination.getScope().equals("singleton")){Object bean = createBean(entry.getKey(), beanDefination);singletonObjects.put(entry.getKey(), bean);     //单例bean}}}public Object createBean(String beanName, BeanDefination beanDefination){Class clazz = beanDefination.getClazz();Object instance = null;try {// 1 对象实例化instance = clazz.newInstance();// 2 依赖注入 属性赋值for(Field field : clazz.getDeclaredFields()){if(field.isAnnotationPresent(LmAutowired.class)){String name = field.getName();  // field.getType()  ,by name/ by typeObject bean = getBean(name);field.setAccessible(true);field.set(instance,bean);}}// 4 初始化前for (BeanPostProcess beanPostProcess : beanPostProcessorList) {instance =  beanPostProcess.postProcessBeforeInitialization(instance,beanName);}// 3 spring初始化数值if(instance instanceof InitializingBean){try {((InitializingBean)instance).afterPropertiesSet();} catch (Exception e) {e.printStackTrace();}}// 5 初始化后for (BeanPostProcess beanPostProcess : beanPostProcessorList) {instance =  beanPostProcess.postProcessAfterInitialization(instance,beanName);}} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return instance;}public void scan(Class configClass){// 1 解析配置类if(configClass.isAnnotationPresent(LmComponentScan.class)){LmComponentScan componentScan = (LmComponentScan)configClass.getAnnotation(LmComponentScan.class);String path = componentScan.value();System.out.println("配置路径: " + path);path = path.replace(".","/");//2 扫描配置路径下有LmComponent注解的类ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();  //appURL resource = classLoader.getResource(path);File file = new File(resource.getFile()); //获取对应文件夹File[] files = file.listFiles();//文件夹下所有的文件for (File f : files) {String fileName = f.getAbsolutePath();if(fileName.endsWith(".class")){String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));className = className.replace("/",".");try {Class clazz = classLoader.loadClass(className);//spring是使用ASM去解析字节码文件的,这里简单实现一下if(clazz.isAnnotationPresent(LmComponent.class)){//beanPostProcessorList初始化if(BeanPostProcess.class.isAssignableFrom(clazz)){try {BeanPostProcess beanPostProcess = (BeanPostProcess) clazz.newInstance();beanPostProcessorList.add(beanPostProcess);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}LmComponent annotation = (LmComponent) clazz.getAnnotation(LmComponent.class);String beanName = annotation.value();//3 生成beanDefination对象BeanDefination beanDefination = new BeanDefination();beanDefination.setClazz(clazz);if(clazz.isAnnotationPresent(Scope.class)){Scope scope = (Scope) clazz.getAnnotation(Scope.class);beanDefination.setScope(scope.value());}else{beanDefination.setScope("singleton");}beanDefinationMap.put(beanName,beanDefination);}} catch (ClassNotFoundException e) {e.printStackTrace();}}}}else {System.out.println("没有LmComponentScan注解");}}public Object getBean(String beanName){//1 判断是否已存在beanif(beanDefinationMap.containsKey(beanName)){BeanDefination beanDefination = beanDefinationMap.get(beanName);//2 判断是不是单例beanif(beanDefination.getScope().equals("singleton")){Object o = singletonObjects.get(beanName);return o;}else{//多例beanObject bean = createBean(beanName,beanDefination);return bean;}}else {System.out.println("没有找到" + beanName);throw new NullPointerException();}}}

2)BeanDefination

public class BeanDefination {private Class clazz;private String scope;public Class getClazz() {return clazz;}public void setClazz(Class clazz) {this.clazz = clazz;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}}

3)BeanPostProcess

public interface BeanPostProcess {Object postProcessBeforeInitialization(Object bean,String beanName);Object postProcessAfterInitialization(Object bean,String beanName);
}

4)InitializingBean

public interface InitializingBean {void afterPropertiesSet() throws Exception;
}

5)注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface LmAutowired {String value() default ""; //属性
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface LmComponent {String value() default ""; //属性
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface LmComponentScan {String value() default ""; //属性
}@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {String value() default ""; //属性
}

6)测试类

public class Main {public static void main(String[] args){//用spring 包里自己写的代码SpringApplicationContext springApplicationContext = new SpringApplicationContext(AppConfig.class);//多例beanUserService userService =  (UserService)springApplicationContext.getBean("userService");UserService userService2 =  (UserService)springApplicationContext.getBean("userService");UserService userService3 =  (UserService)springApplicationContext.getBean("userService");System.out.println(userService);System.out.println(userService2);System.out.println(userService3);userService.test();System.out.println("_____________________");//单例beanOrderService orderService = (OrderService)springApplicationContext.getBean("orderService");OrderService orderService2 = (OrderService)springApplicationContext.getBean("orderService");OrderService orderService3 = (OrderService)springApplicationContext.getBean("orderService");System.out.println(orderService);System.out.println(orderService2);System.out.println(orderService3);}
}

7)BeanPostProcesorImpl

@LmComponent
public class BeanPostProcesorImpl implements BeanPostProcess {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {if(beanName.equals("orderService")){System.out.println("初始化之前");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {//代理AOP在此实现return bean;}
}

8)AppConfig

@LmComponentScan("com.lmlsj.test")
public class AppConfig {
}

9)其他类

@LmComponent("orderService")
public class OrderService {
}@LmComponent("userDao")
public class UserDao {
}@LmComponent("userService")
@Scope("prototype")
public class UserService implements InitializingBean {@LmAutowiredprivate UserDao userDao;private User defaultUser;public void test(){System.out.println(userDao);System.out.println(defaultUser.getName() + ":" + defaultUser.getPass());}@Overridepublic void afterPropertiesSet() throws Exception {defaultUser = new User("default","123456");}
}

10)测试结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/278883.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高级前端开发工程师

岗位需求 熟练掌握前端主流框架Vue、React、Angular,至少熟练掌控Vue全家桶 文章目录 岗位需求前言一、Vue框架二、React框架三、Angular框架四、什么是Vue全家桶前言 -那就看你表哥的电脑里有没有硬盘 -我不敲键盘 一、Vue框架 Vue(读音为/vjuː/,类似于"view"…

Python:如何将MCD12Q1\MOD11A2\MOD13A2原始数据集批量输出为TIFF文件(镶嵌/重投影/)?

博客已同步微信公众号&#xff1a;GIS茄子&#xff1b;若博客出现纰漏或有更多问题交流欢迎关注GIS茄子&#xff0c;或者邮箱联系(推荐-见主页). 00 前言 之前一段时间一直使用ENVI IDL处理遥感数据&#xff0c;但是确实对于一些比较新鲜的东西IDL并没有python那么好的及时性&…

uniGUI学习之Cookie

UniApplication.Cookies.SetCookie( const ACookieName: string, const AValue: string, AExpires: TDateTime 0, ASecure: Boolean False, AHTTPOnly: Boolean False, const APath: string / )

MySQL数据库遇到不规范建表问题解决方案

简介&#xff1a; 需要建立的关联表如上图所示。 问题发现&#xff1a; 好&#xff0c;问题来了&#xff0c;大伙儿请看&#xff1a;我们的organizations表中的Industry字段居然存储了两个IndustryName&#xff0c;这就很恶心了&#xff0c;就需要我们进行拆分和去重后放到In…

精选硬件连通性测试工具:企业如何做出明智选择

在当今数字化的商业环境中&#xff0c;企业的硬件连通性至关重要。选择适用的硬件连通性测试工具是确保网络和设备协同工作的关键一步。本文将探讨企业在选择硬件连通性测试工具时应考虑的关键因素&#xff0c;以帮助其做出明智的决策。 1. 功能全面性&#xff1a;首要考虑因素…

L1-047:装睡

题目描述 你永远叫不醒一个装睡的人 —— 但是通过分析一个人的呼吸频率和脉搏&#xff0c;你可以发现谁在装睡&#xff01;医生告诉我们&#xff0c;正常人睡眠时的呼吸频率是每分钟15-20次&#xff0c;脉搏是每分钟50-70次。下面给定一系列人的呼吸频率与脉搏&#xff0c;请你…

Notes Domino 14.0正式版发布

大家好&#xff0c;才是真的好。 经过12个月的等待&#xff0c;经过三个Beta版本的迭代&#xff0c;昨天晚上11:00&#xff0c;Notes Domino 14.0版本正式发布&#xff01; 过去半年&#xff0c;经过我们对三个Beta版本不断的测试和介绍&#xff0c;一些新功能可能大家已经了…

【Spring】06 生命周期之销毁回调

文章目录 1. 回调是什么2. 销毁回调2.1 实现 DisposableBean 接口2.2 配置 destroy-method 3. 执行顺序4. 应用场景总结 在 Spring 框架中&#xff0c;生命周期回调&#xff08;Lifecycle Callbacks&#xff09;是一种强大的机制&#xff0c;它允许我们在 Spring 容器中的 Bean…

【PTA刷题+代码+详解】求二叉树度为1的结点个数(递归法)

文章目录 题目C代码详解 题目 在二叉树T中&#xff0c;其度为1的结点是指某结点只有左孩子或只有右孩子。利用递归方法求二叉树T的度为1的结点个数。 1&#xff09;如果TNULL&#xff0c;则是空树&#xff0c;度为1的结点个数为0&#xff0c;返回值为0&#xff1b; 2&#xff0…

我的NPI项目之Android 安全系列 -- 先认识一下ST33Jxxx

目前接触过的高通平台都没有集成单独的SE&#xff0c;安全运行环境都是高通自家的TEE&#xff0c;又言Trustzone。高通Keystore功能也是依赖TEE来实现的。那么&#xff0c;如果另外集成SE&#xff0c;那么高通的Keystore如何集成&#xff1f;TEE部分要如何配置&#xff1f; 最近…

银河麒麟本地软件源配置方法

软件源介绍 软件源可以理解为软件仓库&#xff0c;当需要安装软件时则会根据源配置去相应的软件源下载软件包&#xff0c;此方法的优点是可以自动解决软件包的依赖关系。常见的软件源有光盘源、硬盘源、FTP源、HTTP源&#xff0c;本文档主要介绍本地软件源的配置方法&#xff…

前端框架的虚拟DOM(Virtual DOM)

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…