spring的主要内容
背景
- EJB的缺点
- spring的知识
是一个轻量级的Java EE解决方案,整合了众多的设计模式
- 轻量级
对运行的环境没有额外的要求,只需要有servlet的引擎即可以使用,可以选择开源的服务器,如tomcat。也可以选择收费的。代码的可移植性高,不需要实现额外的接口。
- Java EE解决方案
之前学过的structs2和mybatis只是解决分层开发中某一层的问题,spring可以解决分层开发中各个层次的问题。是一个总体的解决方案。在着其中整合了各种的设计模式,正式由于这些设计模式,如工厂模式,模板,观察者模式,代理模式才可以对分层开发中的各个层次进行整合和解决。
设计模式
- 广义:在面向对象的的过程中,解决某中问题的特定代码
- 狭义:GOF4提出的经典23中设计的模式,如工厂,适配器,代理…
工厂设计模式
概念:通过工厂类的方式来创建对象。
优点:可以降低耦合,接口的实现类由工厂进行创建,在分层中,当接口的实现类进行改变的时候,不需要更改另一层。在controller
中通过new serviceimpl的方式,当接口的实现类构造方法进行改变的时候,耦合不利于代码的维护,controller也需要进行改变,通过工厂的方式不需要改变controller,只需要改变工厂即可。
- 没用工厂模式的代码
public interface UserDao {
String login(String username);
}
public class UserDaoImpl implements UserDao {
public String login(String username) {
return String.format("用户名为%s的用户登录成功",username);
}
}
public interface UserService {
//模拟用户登录
String login(String username);
}
public class UserServiceImpl implements UserService {
//在这里使用了new,增加了耦合,当需要更改实现类的时候还需要更改这个操作
private UserDao userDao = new UserDaoImpl();
public String login(String username) {
return userDao.login(username);
}
}
结果测试
@Test
public void testDemo(){
UserService userService = new UserServiceImpl();
System.out.println(userService.login("admin"));
}
- 使用了工厂模式
public class MyBeanFactory {
public static UserService getUserService(){
return new UserServiceImpl();
}
}
UserService userService = MyBeanFactory.getUserService();
System.out.println(userService.login("admin"));
这种情况下还是耦合,只是将耦合转移到了简单工厂里面
- 继续改进,使用配置文件的方式进行,利用反射创建对象
userService = com.demo.service.impl.UserServiceImpl
private static Properties properties = new Properties();
//在加载类时读取配置文件
static {
InputStream inputStream = MyBeanFactory.class.getResourceAsStream("/bean.properties");
try {
properties.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService(){
String userService = (String) properties.get("userService");
UserService service = null;
try {
service = (UserService) Class.forName(userService).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return service;
}
- 终极版改进,通用工厂类,需要更改实现只需要更改配置文件即可
传入配置文件的key,然后根据key去查找要创建的类的值
private static Properties properties = new Properties();
//在加载类时读取配置文件
static {
InputStream inputStream = MyBeanFactory.class.getResourceAsStream("/bean.properties");
try {
properties.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object getBean(String key){
String value = (String) properties.get(key);
Object result = null;
try {
result = Class.forName(value).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
- 通用工厂的使用方式
- 定义类型 (类)
- 通过配置文件的配置告知工厂(applicationContext.properties)
key = value - 通过工厂获得类的对象
Object ret = BeanFactory.getBean(“key”)
spring的基础
- 导入依赖包
#设置pom 依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
- 写配置文件
- 进行相应的测试
每个框架中都有相应的核心类,如mybatis中核心类为sqlsessionfactory,在spring中核心类为application Context,该类是一个接口,主要的两个实现为
classPathApplicationContext
和webApplicationContext
,后者需要又web容器的支持。
该核心类属于重量资源,在创建的时候会占用大量的内存,因此要保证单例和多线程情况下的安全
spring的快速开始(对比通用工厂类)
- 自定义需要的类
- 写配置文件
- 测试
<bean id="userService" class="com.demo.service.impl.UserServiceImpl"></bean>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
System.out.println(userService.login("admin"));
在获取bea的时候可以根据id进行获取,也可以根据类型进行获取,也可以获取所有bean定义的名字
工厂的相关方法
//通过这种方式获得对象,就不需要强制类型转换
Person person = ctx.getBean("person", Person.class);
System.out.println("person = " + person);
//当前Spring的配置文件中 只能有一个<bean class是Person类型
Person person = ctx.getBean(Person.class);
System.out.println("person = " + person);
//获取的是 Spring工厂配置文件中所有bean标签的id值 person person1
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
//根据类型获得Spring配置文件中对应的id值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String id : beanNamesForType) {
System.out.println("id = " + id);
}
//用于判断是否存在指定id值得bean
if (ctx.containsBeanDefinition("a")) {
System.out.println("true = " + true);
}else{
System.out.println("false = " + false);
}
//用于判断是否存在指定id值得bean
if (ctx.containsBean("person")) {
System.out.println("true = " + true);
}else{
System.out.println("false = " + false);
}
配置文件的细节
1. 只配置class属性
<bean class="com.baizhiedu.basic.Person"/>
a) 上述这种配置 有没有id值 com.baizhiedu.basic.Person#0
b) 应用场景: **如果这个bean只需要使用一次,那么就可以省略id值**
如果这个bean会使用多次,或者被其他bean引用则需要设置id值
2. name属性
作用:用于在Spring的配置文件中,为bean对象定义别名(小名)
相同:
1. ctx.getBean("id|name")-->object
2. <bean id="" class=""
等效
<bean name="" class=""
区别:
1. **别名可以定义多个,但是id属性只能有一个值**
2. XML的id属性的值,命名要求:必须以字母开头,字母 数字 下划线 连字符 不能以特殊字符开头 /person
name属性的值,命名没有要求 /person
name属性会应用在特殊命名的场景下:/person (spring+struts1)
XML发展到了今天:ID属性的限制,不存在 /person
3. 代码
//用于判断是否存在指定id值得bean,不能判断name值
if (ctx.containsBeanDefinition("person")) {
System.out.println("true = " + true);
}else{
System.out.println("false = " + false);
}
//用于判断是否存在指定id值得bean,也可以判断name值
if (ctx.containsBean("p")) {
System.out.println("true = " + true);
}else{
System.out.println("false = " + false);
}
配置文件中的一些注意点
a) 上述这种配置 有没有id值 com.baizhiedu.basic.Person#0 b) 应用场景: 如果这个bean只需要使用一次,那么就可以省略id值 如果这个bean会使用多次,或者被其他bean引用则需要设置id值,类似于匿名对象name与id的一些区别
用于在Spring的配置文件中,为bean对象定义别名(小名),name可以有多个,id必须保证唯一。
spring工厂的底层的实现
主要是通过反射,也可以调用私有的构造方法
实体类通常不交给spring进行管理,主要通过DAO进行创建
注入的相关知识
注入的概念
通过spring或者配置文件为对象的属性复制的操作成为注入
优点:通过编码的方式为对象赋值会有耦合,注入可以解耦合,不需要更改代码,更改属性的值的时候不需要更改代码。
注入的实现方式
set
方法
<bean id="person" class="com.baizhiedu.basic.Person">
<property name="id">
<value>10</value>
</property>
<property name="name">
<value>xiaojr</value>
</property>
</bean>
set注入的详解
基本类型
<property name="age">
<value>12</value>
</property>
<property name="name">
<value>张三</value>
</property>
<property name="tel">
<list>
<value>123456</value>
<value>12345</value>
</list>
</property>
<property name="map">
<map>
<entry key="a" value="1"></entry>
<entry key="b" value="2"></entry>
</map>
</property>
<property name="strings">
<array>
<value>aaa</value>
<value>bbb</value>
</array>
</property>
<property name="properties">
<props>
<prop key="a1">1</prop>
<prop key="a2">2</prop>
</props>
</property>
自定义类型
- 使用内部bean
<property name="userDAO">
<bean class="xxx.UserDAOImpl"/>
</property>
- 使用ref
<bean id="userService" class="xxx.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
基于p命名空间的简化
导入p命名空间,然后进行设置相应的值。
<bean id="userService" class="xxx.UserServiceImpl" p:userDAO-ref="userDAO" p:name="张三"/>
- 构造注入(不常用)
主要在框架中中使用。
<bean id="customer" class="com.baizhiedu.basic.constructer.Customer">
<constructor-arg> 一个该标签代表构造方法的一个参数
<value>suns</value>
</constructor-arg>
<constructor-arg>
<value>102</value>
</constructor-arg>
</bean>
反转控制和依赖注入
- 反转控制(ioc)
所谓的控制就是对成员变量的而控制权,之前对成员变量的控制权在我们手上,直接在代码中进行属性的赋值。现在转移到spring框架上。底层实现是工厂设计模式,可以解耦合
。
- 依赖注入
通过spring+配置文件的方式为属性进行赋值。
spring创建复杂的对象
- 何为复杂的对象?
不能直接通过new的方式得到的对象成为复杂的对象。 - 创建的方式?
使用继承factoryBean接口;
public class TestFactoryBean implements FactoryBean<User> {
public User getObject() throws Exception {
User user = new User();
user.setAge(12);
user.setName("这是测试");
return user;
}
public Class<?> getObjectType() {
return User.class;
}
public boolean isSingleton() {
return true;
}
}
相应的配置
<bean id="myBeanFactory" class="com.demo.po.TestFactoryBean"></bean>
如果不想与spring进行耦合,也可以使用静态的方法。
可以通过scope或者isSigton来控制是否单例,有些单例,有些事需要多例