spring2 框架入门
Spring
- IoC(控制反转)/ DI(依赖注入)
- AOP(面向切面编程)
1.创建IOC工程
1.1导入依赖
1 | <dependency> |
IOC框架只需要context依赖就够了
1.2创建实体类
1 | public class Student { |
1.3编写对应的配置文件,Spring.xml(全局)
1 |
|
- 上面的beans是文件头,黏贴就好,然后下面的bean就是写自己的对应的实体类
- bean标签依据id来对应你需要创建的实体类,需要通过class指定好对应的实体类的路径
- 下面的
property就是为值附的属性,如果没有就是默认的结果
1.4主程序调试
1 | public static void main(String[] args) { |
2.配置文件
2.1标准
通过配置
bean标签来完成对象的管理。id:对象名。class:对象的模版类(所有交给 IoC 容器来管理的类必须有无参构造函数,因为 Spring 底层是通过反射机制来创建对象,调用的是无参构造)
对象的成员变量通过
property标签完成赋值。name:成员变量名。value:成员变量值(基本数据类型,String 可以直接赋值,如果是其他引用类型,不能通过 value 赋值)ref:将 IoC 中的另外一个 bean 赋给当前的成员变量(DI)
2.2引用类型
新的数据类型
public class Address { private long id; private String name; }1
2
3
4
5
6
7
8
- ```java
public class Student {
private long id;
private String name;
private int age;
private Address address;
}
在xml配置文件中添加新的bean
<bean id="address" class="com.Lei.pojo.Address"> <property name="id" value="1"></property> <property name="name" value="科技路"></property> </bean>1
2
3
4
5
6
7
8
9
- ```xml
<!--原来的实体类xml中是引用数据类型就通过ref标签调用xml中现在新加入的bean-->
<bean id="student" class="com.Lei.pojo.Student">
<property name="id" value="0"></property>
<property name="name" value="张三"></property>
<property name="age" value="10"></property>
<property name="address" ref="address"></property>
</bean>
主程序调试
public static void main(String[] args) { ApplicationContext applicationContext =new ClassPathXmlApplicationContext("Spring.xml"); Student student = (Student) applicationContext.getBean("student"); System.out.println(student); //一样的代码,降低了耦合度 } //结果:Student(id=0, name=张三, age=10, address=Address(id=1, name=科技路))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
### 3模拟IOC底层
> 思路
>
> 1. 解析xml配置文件
> 2. 得到里面的元素,遍历获取对应的id、class路径
> 3. 用类解析反射出整个类,获取构造器,创建对象
> 4. 通过Map键值对存储获取的id和整个类对象
> 5. getBean方法就是通过id来取Map中的对象
- 导入dom4j依赖
- ```xml
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
创建接口
//写的接口名和spring给的一样 public interface ApplicationContext { public Object getBean(String id); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
- 编写解析类
- ```java
public class ClassPathXmlApplicationContext implements ApplicationContext {
Map<String, Object> Ioc = new HashMap<>();//键值对形式存储
public ClassPathXmlApplicationContext(String path) {
SAXReader saxReader = new SAXReader();//dom4j读入
try {
Document read = saxReader.read("./src/main/resources/" + path);//读取xml配置文件
Element rootElement = read.getRootElement();//获取里面的元素 打印出来结果 Element: <beans>
//
Iterator<Element> iterator = rootElement.elementIterator();
//获取迭代器遍历
while (iterator.hasNext()){
Element element = iterator.next();
String id = element.attributeValue("id");
String ClassName = element.attributeValue("class");
//找到id、class字段
Class aClass = Class.forName(ClassName);
//通过ClassName反射出实体类
Constructor constructor = aClass.getConstructor();
//获取实体类的构造函数
Object o = constructor.newInstance();
//通过构造函数创建对象
Ioc.put(id,o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Object getBean(String id) {
return Ioc.get(id);
}
}
获取对应的property字段值
原理再次嵌套一个element元素的遍历,获取相应的字段,然后通过invoke赋值给原来的对象
public ClassPathXmlApplicationContext(String path) { SAXReader saxReader = new SAXReader();//dom4j读入 try { Document read = saxReader.read("./src/main/resources/" + path);//读取xml配置文件 Element rootElement = read.getRootElement();//获取里面的元素 打印出来结果 Element: <beans> // Iterator<Element> iterator = rootElement.elementIterator(); //获取迭代器遍历 while (iterator.hasNext()) { Element element = iterator.next(); String id = element.attributeValue("id"); String ClassName = element.attributeValue("class"); //找到id、class字段 Class aClass = Class.forName(ClassName); //通过ClassName反射出实体类 Constructor constructor = aClass.getConstructor(); //获取实体类的构造函数 Object o = constructor.newInstance(); //通过构造函数创建对象 Iterator<Element> bean = element.elementIterator(); while (bean.hasNext()) { Element property = bean.next(); String name = property.attributeValue("name"); String valueStr = property.attributeValue("value"); //获取字段 String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); //找到set的方法 Field field = aClass.getDeclaredField(name); Method declaredMethod = aClass.getDeclaredMethod(methodName, field.getType()); //反射出方法和field Object value = null; //下面如果不指定类型会产生类型不匹配错误 if (field.getType().getName().equals("long")){ value =Long.parseLong(valueStr); } if (field.getType().getName().equals("java.lang.String")){ value =valueStr; } if (field.getType().getName().equals("int")){ value =Integer.parseInt(valueStr); } //每次invoke赋值 declaredMethod.invoke(o,value); } Ioc.put(id, o); } } catch (Exception e) { e.printStackTrace(); } }1
2
3
4
5
6
7
8
9
### 4.bean的调用
#### 4.1通过运行时类获取 bean
```java
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student student = (Student) applicationContext.getBean(Student.class);
System.out.println(student);
这种方式存在一个问题,配置文件中一个数据类型的对象只能有一个实例,否则会抛出异常,因为没有唯一的 bean。还是推荐直接用id获取bean;
4.2通过有参构造创建 bean通过有参构造创建 bean
- 在实体类中创建对应的有参构造函数。
- 三种方式,推荐下面第一个,如果不写name或者index就需要按对应顺序写
1 | <bean id="student3" class="com.Lei.pojo.Student"> |
1 | <bean id="student3" class="com.Lei.pojo.Student"> |
4.3给bean注入集合
将原来的实体类换成集合
public class Student { private long id; private String name; private int age; private List<Address> addresses; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- 重新配置xml
- ```xml
<?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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<bean id="student" class="com.Lei.pojo.Student">
<property name="id" value="0"></property>
<property name="name" value="张三"></property>
<property name="age" value="10"></property>
<property name="addresses" >
<!--这里将addresses写入一个集合里-->
<list>
<ref bean="address01"></ref>
<ref bean="address02"></ref>
</list>
</property>
</bean>
<bean id="address01" class="com.Lei.pojo.Address">
<property name="id" value="1"></property>
<property name="name" value="科技路"></property>
</bean>
<bean id="address02" class="com.Lei.pojo.Address">
<property name="id" value="2"></property>
<property name="name" value="高新区"></property>
</bean>
</beans>
5.作用域
Spring 管理的 bean 是根据 scope 来生成的,表示 bean 的作用域,共4种,默认值是 singleton。
- singleton:单例,表示通过 IoC 容器获取的 bean 是唯一的。
- prototype:原型,表示通过 IoC 容器获取的 bean 是不同的。
- request:请求,表示在一次 HTTP 请求内有效。
- session:回话,表示在一个用户会话内有效。
request 和 session 只适用于 Web 项目,大多数情况下,使用单例和原型较多。
特点:
- prototype 模式当业务代码获取 IoC 容器中的 bean 时,Spring 才去调用无参构造创建对应的 bean。能优先判断是否需要创建,只有需要的时候才会创建,但是创建要更多空间。
- singleton 模式无论业务代码是否获取 IoC 容器中的 bean,Spring 在加载 spring.xml 时就会创建 bean。耗资源省时间
6.Spring继承
- 与 Java 的继承不同,Java 是类层面的继承,子类可以继承父类的内部结构信息;Spring 是对象层面的继承,子对象可以继承父对象的属性值。
- Spring 的继承关注点在于具体的对象,而不在于类,即不同的两个类的实例化对象可以完成继承,前提是子对象必须包含父对象的所有属性,同时可以在此基础上添加其他的属性。
xml文件配置
直接用parent字段就可以实现继承并且将原来的值全部赋好
<bean id="student02" class="com.Lei.pojo.Student" parent="student"></bean>1
2
3
4
5
6
7
8
9
10
- 测试
- ```java
public static void main(String[] args) {
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("Spring.xml");
Student student = (Student) applicationContext.getBean("student02");
System.out.println(student);
}
//结果:Student(id=0, name=张三, age=10, addresses=[Address(id=1, name=科技路), Address(id=2, name=高新区)])
7.Spring依赖
与继承类似,依赖也是描述 bean 和 bean 之间的一种关系,配置依赖之后,被依赖的 bean 一定先创建,再创建依赖的 bean,A 依赖于 B,先创建 B,再创建 A。
创建依赖类
public class User { private long id; private String name; public User() { System.out.println("创建了User"); } }1
2
3
4
5
- xml配置
- ```xml
<bean id="user" class="com.Lei.pojo.User" depends-on="student"></bean>
如果不加依赖就是按照xml的顺序直接创建,加了依赖一定先创建依赖的类
8.Spring 的 p 命名空间
p 命名空间是对 IoC / DI 的简化操作,使用 p 命名空间可以更加方便的完成 bean 的配置以及 bean 之间的依赖注入。
1 | <bean id="student" class="com.southwind.entity.Student" p:id="1" p:name="张三" p:age="22" p:address-ref="address"></bean> |
其实和property一样,简化写法
9.Spring工厂
IoC 通过工厂模式创建 bean 的方式有两种:
静态工厂
创建实例类
public class Car { private long id; private String name; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
- 创建静态工厂类
- ```java
public class StaticFactory {
private static Map<Long,Car> carMap =new HashMap<>();//通过Map键值对直接存储静态内容
static {
carMap.put(1L,new Car(1L,"宝马"));
carMap.put(2L,new Car(2L,"奔驰"));
}
public static Car getCar(Long id){
return carMap.get(id);
}
}
编写xml
<!-- 配置静态工厂创建 Car --> <bean id="car" class="com.Lei.pojo.StaticFactory" factory-method="getCar" > <constructor-arg value="1"></constructor-arg> </bean>1
2
3
4
5
6
7
8
9
10
- 主程序测试
- ```java
public static void main(String[] args) {
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("Spring-factory.xml");
Car car = (Car) applicationContext.getBean("car");
System.out.println(car);
}
//结果:Car(id=1, name=宝马)
实例工厂
创建实例工厂类->通过无参构造来创建对象
public class InstanceCarFactory { Map<Long, Car> carMap = new HashMap<>(); public InstanceCarFactory() { carMap.put(1L, new Car(1L, "宝马")); carMap.put(2L, new Car(2L, "奔驰")); } public Car getCar(Long id) { return carMap.get(id); } }1
2
3
4
5
6
7
8
- 编写xml
- ```xml
<bean id="CarFactory" class="com.Lei.pojo.InstanceCarFactory"></bean>
<bean id="car" factory-bean="CarFactory" factory-method="getCar">
<constructor-arg value="1"></constructor-arg>
</bean>由于创建实例工厂需要首先工厂的无参构造执行,所以需要两个bean
主程序测试
public static void main(String[] args) { ApplicationContext applicationContext =new ClassPathXmlApplicationContext("Spring-factory.xml"); Car car = (Car) applicationContext.getBean("car"); System.out.println(car); } //结果:Car(id=1, name=宝马)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
### 10.IoC 自动装载(Autowire)
IoC 负责创建对象,DI 负责完成对象的依赖注入,通过配置 property 标签的 ref 属性来完成,同时 Spring 提供了另外一种更加简便的依赖注入方式:自动装载,不需要手动配置 property,IoC 容器会自动选择 bean 完成注入。
自动装载有两种方式:
- byName:通过属性名自动装载,找实体类中属性名与bean中对应的id
- byType:通过属性的数据类型自动装载,找同一致的数据类型
```xml
<bean id="cars" class="com.southwind.entity.Car">
<property name="id" value="1"></property>
<property name="name" value="宝马"></property>
</bean>
<bean id="person" class="com.southwind.entity.Person" autowire="byName">
<property name="id" value="11"></property>
<property name="name" value="张三"></property>
</bean>
byType 需要注意,如果同时存在两个及以上的符合条件的 bean 时,自动装载会抛出异常。
11.Aop切面
AOP:Aspect Oriented Programming 面向切面编程。
AOP 的优点:
- 降低模块之间的耦合度。
- 使系统更容易扩展。
- 更好的代码复用。
- 非业务代码更加集中,不分散,便于统一管理。
- 业务代码更加简洁存粹,不参杂其他代码的影响。
AOP 是对面向对象编程的一个补充,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程。将不同方法的同一个位置抽象成一个切面对象,对该切面对象进行编程就是 AOP。
具体类比实现:
- 导入依赖
1 | <dependency> |
- 编写需要实现切面的接口
1 | public interface Cal{ |
- 实现接口的实体类
1 | //添加注解可以直接在spring.xml中获得bean,如果括号里有参数那么就将id映射 |
- 编写抽象出来的切面类
1 |
|
- 关于execution表达式
| 标识符 | 含义 |
|---|---|
| execution() | 表达式的主体 |
| 第一个“*”符号 | 表示返回值的类型任意 |
| com.Lei.pojo.CalImpl | AOP所切的服务的包名,即,需要进行横切的业务类 |
| 包名后面的“..” | 表示当前包及子包 |
| .*(..) | 表示任何方法名,括号表示参数,两个点表示任何参数类型 |
切面出来的JoinPoint连接点,那么就是可以用来get各种参数的方法
分别有四种注解Before,After,AfterReturning,AfterThrowing分别用来表示执行前,后,执行的结果集,执行出的异常
- 编写切面的xml
1 |
|
context:component-scan自动扫描包里的含有component的字段生成beanaop:aspectj-autoproxyaop自动的动态代理











