Spring 理解

        Spring 是一个轻量级框架,主要解决了创建对象和管理对象的相关问题.Spring会在创建对象之后,完成必要的属性赋值,并且还持有所创建对象的引用,由于大量的对象引用,所以,被称为Spring容器.

        IOC就是Spring的核心功能之一,它是帮助程序员创建对象,称之为反转,IOC组件注入的时候自动匹配规则是先按类型再按ID.

        1. 首先按照注入参数类型查找相应类型的Bean组件

        2. 如果在Spring容器中能够匹配上唯一类型的Bean组件,则进行注入成功

        3. 如果按照类型匹配到两个Bean组件,则在查找组件ID和变量名是否匹配,如果匹配则注入成功

        4. 如果组件类型和组件ID都不能很好匹配则报错误.

Spring 创建对象的做法

        Spring 创建对象有两种,第一种是通过配置类的@Bean方法,第二种是组件扫描

        关于@Bean注解,在任何配置类中,自定义返回对象的方法,并在方法上添加@Bean注解,则Spring会自动调用此方法,并且获取方法返回的对象,将对象保存在Spring容器中

@Configuration
public class BeanFactory {
    
    @Bean
    public LocalDateTime localDateTime() {
        return LocalDateTime.now();
    }
    
    // 如果使用这种做法,则AlbumController不必使用组件扫描的做法
    @Bean
    public AlbumController albumController() {
        return new AlbumController();  
    }
    
    // 如果使用这种做法,则AlbumServiceImpl不必使用组件扫描的做法
    @Bean
    public AlbumServiceImpl albumServiceImpl() {
        return new AlbumServiceImpl();  
    }
    
}

        关于组件扫描,需要通过@ComponentScan注解来扫描根包,则Spring框架会在此根包下查找组件,并创建这些组件的对象.

组件在Spring中添加了@Compnent及其衍生注解的都是常用组件

常见组件的注解有:

@Component --- 通用组件注解

@Controller --- 添加在控制器类上

@Service --- 添加在业务逻辑类型

@Repository --- 添加在处理数据访问(直接与数据源交互)的类上

@Configuration --- 添加在配置类上

        以上注解除了@Configuration以外,其他的功能,用法,执行效果方面在框架中完全相同,只是语义不同.

        Spring框架在处理@Configuration注解时,会使用CGLib的代理模式来创建对象,并且,被Spring实际使用的是代理对象。

        在实际实践中如果需要创建非自定义类,的对象,必须使用@Bean方法,如果需要自定义对象,建议使用组件注解,更简单一些.

Spring管理的对象作用域

Spring 管理的对象默认是单例的,则在整个程序的运行过程中,随时可以获取或访问Spring容器中的'单例'对象

Spring并没有实际使用单例模式

单例:单一实例(单一对象),即: 在任意时间,某个类的对象最多只有一个

如果需要Spring管理某个对象采取'非单例模式'可以通过@Scope("prototype")注解来实现!

        如果是通过@Bean方法创建对象,则@Scope("prototype")注解添加在@Bean方法上,如果是通过组件扫描创建对象,则@Scope("prototype")注解添加在组件类上。

如果没有Spring 则需要自己创建单例模式,分为饿汉式和懒汉式

// 饿汉式
// Spring 默认饿汉式
// 饿汉式启动时就需要创建对象
public class Singleton {
    private static Singleton instance = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return instance;
    }
}

// 懒汉式
// 懒汉式启动时也不创建,需要调用的时候才会给你创建
// 这种情况不建议,比如线程繁忙的时候刚好需要这时候才去创建就会造成压力
public class Singleton {
    private static final Object lock = new Object();
    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Spring管理的单例对象,默认情况下预加载的!可以通过@Lazy注解配置为懒加载的!

        如果是通过@Bean方法创建对象,则@Lazy朱朱姐添加在@Bean方法上,如果通过组件扫描创建对象,则@Lazy注解添加在组件上.

自动装配机制

        Spring的自动装配机制 表现为:当Spring管理类的属性需要被自动赋值,或Spring调用的方法参数性需要值是,可以在属性上添加@Autowired注解.

关于方法被调用主要表现为:构造方法,配置类中的@Bean方法等

调用构造方法

        如果类中存在无参构造方法,Spring会自动调用无参构造方法       

        如果类中仅有一个构造方法,Spring会自动尝试调用,且如果构造方法有参数,Spring会自动尝试从容器中找到合适的值调用此构造方法

        如果类中有多个构造方法,且都是有参数的,Spring不会自动调用任何构造方法,且会报错.

@Aytowired的自动装配注解机制

        如果Spring调用特定构造方法,应该在那一个构造方法上添加 @Autowired注解

关于属性上使用@Autowired是提示Field injection is not recommended,其意思是“字段注入是不推荐的”

        因为开发工具认为你有可能在某些情况下创建当前类的对象,例如自行编写实例化对象,由于自行创建对象,Spring框架在此过程中是不干预的,则类的属性接口将不会有Spring注入值.如果此时你也没有为这个属性赋值,则这个属性就是null,如果还执行类中的方法,就可能导致NPE异常.

        这种情况可能发生在单元测试中。开发工具建议使用构造方法注入,即使用带参数的构造方法,且通过构造方法为属性赋值,并且类中只有这1个构造方法,在这种情况下,即使自行创建对象,由于唯一的构造方法是带参数的,所以,创建对象时也会为此参数赋值,不会出现属性没有值的情况,所以,通过构造方法为属性注入值的做法被认为是安全的,是建议使用的做法!

        但是,在开发实践,通常并不会使用构造方法注入属性的值,因为,属性的增、减都需要调整构造方法,并且,如果类中需要注入值的属性较多,也会导致构造方法的参数较多,不是推荐的!

关于适合的值: Spirng框架会查找容器中匹配类型的对象数量

0个无法装配

1个自动装配成功

        超过一个:尝试按照名称来匹配.如果均不匹配,则在加载Spring是直接报错,NoUniqueBeanDefinitonException,按照名称匹配时,要求被装配的变量名与Bean Name保持一致

关于Bean Name

        每个Spring Bean都有一个Bean Name,如果是通过@Bean方法创建的对象,则Bean Name就是方法名,或通过@Bean注解参数来指定名称,

        如果是通过组件扫描的做法来创建的对象,则Bean Name默认是将类名首字母改为小写的名称(例如,类名为AlbumServiceImpl,则Bean Name为albumServiceImpl)(此规则只适用于类名中第1个字母大写、第2个字母小写的情况,如果不符合此情况,则Bean Name就是类名),Bean Name也可以通过@Component等注解的参数进行配置,或者,你还可以在需要装配的属性上使用@Qualifier注解来指定装配哪个Bean Name对应的Spring Bean。

        另外,在处理属性的自动装配上,还可以使用@Resource注解取代@Autowired注解,@Resource是先根据名称尝试装配,再根据类型装配的机制!

Spring管理对象的声明周期

使用组件类时,

        初始化方法上添加@PostConstruct

        销毁方法上添加@PreDestroy

使用@Bean方法时

        初始化方法配置@Bean注解的initMethod

        销毁方法配置@Bean注解destroyMethod

Spring AOP

        AOP是面向切面编程,实现了横切关注的相关问题,通常是许多不同的数据处理流中都可以解决的问题.

        可能处理过程中的处理请求不同,但是需要执行的一些高度相似或完全相同的代码,在某些特定的执行时间点,可以通过一些特殊的组件来完成.

        例如: java EE中的Filter.Spring MVC的Interceptor,Mybatis的Interceptor,但是这些特殊的组件只能在特定时间点执行.

        例如: Filter 是在服务器接收到请求的第一时间就已经执行Spring MVC的Interceprot是在Controller的前后执行,Mybatis的Intercepror是在处理SQL语句是执行,如果需要在其他执行时间节点处理相关的任务,这些组件都是不可用的!

使用AOP通常解决一下类型的问题: 安全.事务管理.日志等等

AOP技术本身并不是Spring特有的技术.只有Spring很好的支持了AOP

        创建用于统计业务耗时切面类,这种切面类本身是一个组件类,并且需要添加@Aspect注解,则在跟包下创建

@Slf4j
@Aspect
@Component
public class TimerAspect {
​
    // 在AOP中,有多种Advice(通知)
    // @Around:包裹,可以实现在连接点之前和之后均自定义代码
    // @Before:在连接点之前执行
    // @After:在连接点之后执行,无论是正常返回还是抛出异常都会执行
    // @AfterReturning:在连接点返回之后执行
    // @AfterThrowing:在连接点抛出异常之后执行
    // 仅当使用@Around时,方法才可以自行处理ProceedingJoinPointer
    // 各Advice的执行大概是:
    // @Around
    // try {
    //   @Before
    //   连接点方法
    //   @AfterReturning
    // } catch (Throwable e) {
    //   @AfterThrowing
    // } finally {
    //   @After
    // }
    // @Around
    // ---------------------------------------------------
    // 关于ProceedingJoinPoint
    // 必须调用proceed()方法,表示执行表达式匹配到的方法
    // 调用proceed()方法必须获取返回值,且作为当前方法的返回值,表示返回表达式匹配的方法的返回值
    // 调用proceed()方法时的异常必须抛出,不可以使用try...catch进行捕获并处理
    // ---------------------------------------------------
    // 关于execution表达式:用于匹配在何时执行AOP相关代码
    // 表达式中的星号:匹配任意内容,只能匹配1次
    // 表达式中的2个连续的小数点:匹配任意内容,可以匹配0~n次,只能用于包名和参数列表部分
    // 表达式中的包是根包,会自动包含其子孙包中的匹配项
    @Around("execution(* cn.tedu.csmall.product.service.*.*(..))")
    //                 ↑ 无论方法的返回值类型是什么
    //                                                  ↑ 无论是哪个类
    //                                                     ↑ 无论是哪个方法
    //                                                       ↑ 2个小数点表示任何参数列表
    public Object timer(ProceedingJoinPoint pjp) throws Throwable {
        log.debug("执行了TimerAspect中的方法……");
​
        long start = System.currentTimeMillis();
        Object result = pjp.proceed(); // 执行连接点方法,获取返回结果
        long end = System.currentTimeMillis();
​
        log.debug("【{}】类型的对象调用了【{}】方法,方法的参数值为【{}】",
                pjp.getTarget().getClass().getName(),
                pjp.getSignature().getName(),
                pjp.getArgs());
        log.debug("执行耗时:{}毫秒", end - start);
​
        return result; // 返回调用pjp.proceed()时的结果
    }

切面是无侵入性的,在不修改其他类的代码情况下,就可以作用于整个数据处理过程!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值