0%

饿汉模式

在项目一开始就初始化对象。

1
2
3
4
5
6
7
8
9
public class Hungry {
private final static Hungry HUNGRY = new Hungry();
private Hungry() {

}
public static Hungry getInstance() {
return HUNGRY;
}
}

使用私有的构造方式确保对象无法在外部被实例化。使用一个静态方法获取内部的静态实例。

不足:的是如果对象太大,会造成大量资源被占用。

懒汉模式

在需要时实例化对象。

1
2
3
4
5
6
7
8
9
10
11
12
public class Lazy {
private static Lazy LAZY;
private Lazy() {

}
public static Lazy getInstance() {
if (LAZY == null) {
LAZY = new Lazy();
}
return LAZY;
}
}

此时线程不安全

双重检验锁的懒汉模式

Double-Checked locking Lazy Mode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LazyDCL {
private volatile static LazyDCL LAZYDCL;
private LazyDCL() {

}
public static LazyDCL getInstance() {
if (LAZYDCL == null) {
synchronized (LazyDCL.class) {
if (LAZYDCL == null) {
LAZYDCL = new LazyDCL();
}
}
}
return LAZYDCL;
}
}

此时线程安全,但还是有问题new LazyDCL()的时候不是原子操作

实例化需要经过的步骤:

  1. 分配内存空间
  2. 执行构造方法 初始化对象
  3. 把这个对象指向这个空间

也就会在底层,CPU执行时发生指令重排的错误。

1->2->3(正确)

1->3->2

如果有以上的顺序,当线程A执行到3步骤,此时如果有线程B进来,则会误判LAZYDCL==null从而直接返回。由于线程A并没有完成实例化的操作,因此可能会发生错误。

注意:此时必须对LAZYDCL加上volatile从而避免指令重排而产生的错误!!!

终极懒汉模式

1.0

破坏方式:通过反射可以强行改变构造器的可视性。以此再来新建一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UltimateLazy {

private volatile static UltimateLazy ULTIMATELAZY;

private UltimateLazy() {
synchronized (UltimateLazy.class) {
if (ULTIMATELAZY != null) {
throw new RuntimeException("error");
}
}
}

private static UltimateLazy getInstance() {
...
}
}

解决方案:但是可以再在私有的构造方法里再加一层判断

2.0

破坏方式:如果都使用反射来破坏单例模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class UltimateLazy {

private volatile static UltimateLazy ULTIMATELAZY;

private UltimateLazy() {
synchronized (UltimateLazy.class) {
if (flag == false) {
flag = true;
} else {
throw new RuntimeException("error");
}
}
}

private static UltimateLazy getInstance() {
...
}
}

解决方案:可以加一个标志位来确保单例。

3.0 终极解决方案

破坏方式:使用反射来改变标志位的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public enum EnumLazy {
INSTANCE;

public EnumLazy getInstance() {
return INSTANCE;
}
}

class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumLazy instanceA = EnumLazy.INSTANCE;
Constructor<EnumLazy> constructor = EnumLazy.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
EnumLazy instanceB = constructor.newInstance();

System.out.println(instanceA == instanceB);
}
}

解决方案:使用单例模式来解决。看源码可以得知,不能使用反射来构建枚举类


Reference

https://www.bilibili.com/video/BV1K54y197iS

Bean Scope

![image-20210201120028673](/Users/edlison/Library/Application Support/typora-user-images/image-20210201120028673.png)

Bean默认的作用域是单例模式,也就是内存中只有一个实例。

Bean的自动装配

自动装配是Spring满足Bean的依赖的一种方式。Spring会在上下文中寻找并自动给Bean装配属性。

Spring有三种装配方式

  • xml中显式的装配
  • java中显示的装配
  • 隐式的自动装配

自动装配byName

要保证Bean的id唯一,Spring会在上下文中寻找和自己对象所需要Setter注入属性的相同id的Bean。

1
<bean name="peopleAutoByName" class="com.edlison.design.spring.ioc.pojo.People" autowire="byName"/>

自动装配byType

要保证Bean的类型唯一,Spring会在上下文中寻找自己对象需要Setter注入的值的类型相同的Bean。

1
<bean name="peopleAutoByType" class="com.edlison.design.spring.ioc.pojo.People" autowire="byType"/>

通过注解自动装配

首先引入规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>

<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>

<bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

对需要自动装配的对象打上注解@Autowired

1
2
3
4
5
6
7
8
9
public class People {
@Autowired
@Qualifier(value = "dog2")
private Dog dog;

@Autowired
@Qualifier(value = "cat")
private Cat cat;
}

@Autowired可以通过byType或byName的方式实现自动装配。

注解开发

使用注解首先要引入context约束,Spring4.0后还需要AOP的包。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config/>
<context:component-scan base-package="com.edlison.design.spring.ioc"/>

</beans>

1. Bean

1
2
3
4
5
6
7
8
9
10
@Component
public class Role {

@Value("admin")
private String role_name;

public String getRole_name() {
return role_name;
}
}

@Component相当于

1
<bean name="role" class="com.edlison.design.spring.ioc.pojo.Role"/>

注意:
注入后的Bean的name为相应的驼峰形式。

2. 属性的注入

1
2
3
4
5
6
7
8
9
10
@Component
public class Role {

@Value("admin")
private String role_name;

public String getRole_name() {
return role_name;
}
}

@Value相当于

1
<property name="username" value="This is username"/>

3. 衍生的注解

@Component有几个衍生注解,在Web开发中有MVC三层架构分层。

  • @RepositoryDAO层
  • @ServiceService层
  • @ControllerController层

这些注解与@Component完全一致,只是Alias.

4. 自动装配

见上面

5. 作用域

1
2
3
4
5
6
7
8
9
10
11
@Component
@Scope("prototype")
public class Role {

@Value("admin")
private String role_name;

public String getRole_name() {
return role_name;
}
}

@Scope相当于

1
<bean id="role" class="com.edlison.design.spring.ioc.pojo.Role" scope="prototype">

6. 总结

  • xml更万能适用于任何场合。维护简单方便。
  • 注解不是自己的类使用不了,维护复杂。

最佳实践:

  • xml用来管理Bean
  • 注解用来注入属性

注解开发-完全舍弃XML

完全使用Java类实现配置。

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
@ComponentScan("com.edlison.design.spring.ioc.pojo")
@Import(AppConfigPlus.class)
public class AppConfig {

@Bean
@Scope("prototype")
public Student getStu() {
return new Student();
}

@Bean
public Card card() {
return new Card();
}

@Autowired
public Student student;
}

@Configuration会使Spring容器托管,注册到容器中,它本身也有@Component`。

@Configuration是一个配置类,相当于xml开发时的applicationContext.xml

因此这个类可以配置各种在xml文件中的配置,比如@ComponentScan可以用来扫描指定包下的@Component@Import可以导入其他配置类,导入其他配置类中注册的Bean

@Scope可以对Bean的作用域做出规定。

还可以通过@Autowired直接对Bean进行装载。

Bean对象

1
2
3
4
5
6
7
8
9
@Component
public class Student {
@Value("edlison") // 字符对象
private String name;
@Value("#{card}") // 复杂类
private Card card;
@Autowired
private Bike bike;
}

@Value可以对任何类型的属性进行注入。

也可以通过@Autowired对复杂类型进行自动装配。

测试类

1
2
3
4
5
6
@Test
public void testConfig() {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Student student = context.getBean("getStu", Student.class);
System.out.println(student);
}

完全使用配置类,需要通过AnnotationConfig上下文来获取容器,通过传入配置类的class对象加载。

也可以直接获取config类,通过其方法获取实例。

总结

Spring的IOC机制实质上就是一个容器,统一管理项目开发中的各种类型,可以很方便的控制其是单例模型还是远行模型。

自动装配机制很大的简化了依赖注入,可以直接匹配名称或类型,使得开发进一步简化。

问题

如果一个对象有一个复杂的数据成员对象,且该对象已经装配为Bean,也可以用@Autowired对其进行自动装配?与@Value功能/实质一样?

依赖注入

构造器注入

见上一篇

在创建对象的时候注入。

Setter方法注入

可以注入任何的类型,如Bean(复杂对象),一般类型,集合等数据结构。

String

1
<property name="username" value="This is username"/>

Bean

1
<property name="address" ref="address"/>

Array

1
2
3
4
5
6
7
<property name="books">
<array>
<value>Java</value>
<value>Python</value>
<value>Scala</value>
</array>
</property>

List

1
2
3
4
5
6
<property name="hobbies">
<list>
<value>movies</value>
<value>tennis</value>
</list>
</property>

Set

1
2
3
4
5
6
7
<property name="games">
<set>
<value>SC2</value>
<value>LOL</value>
<value>CSGO</value>
</set>
</property>

Map

1
2
3
4
5
6
<property name="cards">
<map>
<entry key="idcard" value="320325"/>
<entry key="stucard" value="1801"/>
</map>
</property>

Properties

1
2
3
4
5
<property name="info">
<props>
<prop key="Title">CEO</prop>
</props>
</property>

Null

1
2
3
<property name="wife">
<null>null_value</null>
</property>

注意

发现对Array或List赋值,标签<array>list可以互换,没有影响。

扩展方式注入

P命名空间

首先引入规范

1
xmlns:p="http://www.springframework.org/schema/p"

其实就对应Setter方法注入,只不过简化了,使用p:.

1
<bean id="userServicePlus" class="com.edlison.design.spring.ioc.service.UserService" p:userDAO-ref="user" p:userServiceLog="This is a log."/>

C命名空间

首先引入规范

1
xmlns:c="http://www.springframework.org/schema/c"

其实就对应构造器注入,只不过简化了,使用c:.

使用有参构造。

1
<bean id="addressPlus" class="com.edlison.design.spring.ioc.pojo.Address" c:city="Nanjing"/>

IOC的本质

![image-20210201102417224](/Users/edlison/Library/Application Support/typora-user-images/image-20210201102417224.png)

![image-20210201102430827](/Users/edlison/Library/Application Support/typora-user-images/image-20210201102430827.png)

![image-20210201102454419](/Users/edlison/Library/Application Support/typora-user-images/image-20210201102454419.png)

IOC创建对象的方式

使用配置文件设置变量的值。将类注册为 Bean,通过ApplicationContext获取上下文。再用上下文取出Bean

变量赋值

简单的通过Setter方法赋值。

  1. 直接赋值
1
2
3
<bean id="user" class="com.edlison.design.spring.ioc.pojo.UserDAO">
<property name="username" value="this is username"/>
</bean>
  1. 引用
1
2
3
<bean id="userService" class="com.edlison.design.spring.ioc.service.UserService">
<property name="userDAO" ref="user"/>
</bean>

创建对象

默认是无参构造。如果有构造函数,则有以下三种构造方式。

  1. 通过下标构造
1
2
3
<bean id="user" class="com.edlison.design.spring.ioc.pojo.UserDAO">
<constructor-arg index="0" value="this is username by constructor"/>
</bean>
  1. 通过类型构造
  2. 直接通过参数名
1
2
3
<bean id="user" class="com.edlison.design.spring.ioc.pojo.UserDAO">
<constructor-arg name="username" value="this is username by constructor"/>
</bean>

总结

在配置文件加载的时候,容器里的实例就已经被创建了,并且内存中仅有一个实例

But poetry, beauty, romance, love… these are what we stay alive for.

但是,诗歌,美,浪漫,爱。这些是我们活着的意义。

Most men lead lives of quiet desperation.

大多数人,都生活在平静的绝望中。

To quote from Whitman, “O me, O life of the questions of these recurring. Of the endless trains of the faithless. Of cities filled with the foolish. What good amid these, O me, O life? Answer: That you are here. That life exists and identity. That the powerful play goes on and you may contribute a verse. That the powerful play goes on, and you can contrubute a verse.” What will your verse be?

惠特曼写道:“自我?生命?这些问题总在不停出现。毫无信仰的人川流不息。城市充斥着愚昧。生活其中有什么意义?自我?生命?答案:因为你在这里。因为生命存在并各有特性。因为伟大的戏剧在继续,并且你可以奉献一段。因为伟大的戏剧在继续,并且你可以奉献一段。你的段落会是什么样?”

于2021.1.30 0:55:30 与章章 她在家里我在南京宿舍